Was & welche Variante
Nextcloud = Dateien-Sync (wie Dropbox) + Kalender/Kontakte (CalDAV/CardDAV) + Fotos + optional Office, Notizen, Aufgaben, Talk. Selbst gehostet, auf deiner Platte.
| Weg | für wen | Kompromiss |
|---|---|---|
| Nextcloud AIO (All-in-One) | Haushalt, wenig Docker-Erfahrung | ein Mastercontainer verwaltet alles (DB, Redis, Collabora, Backup) – bequem, aber weniger Kontrolle |
| Manuelles Compose (diese Anleitung) | du willst jeden Baustein sehen, Traefik teilen | mehr Handarbeit, dafür volle Kontrolle & Integration in den bestehenden Stack |
| Managed (Hetzner Managed Nextcloud, IONOS, …) | null Server-Arbeit | teurer, weniger Freiheit – aber Überlegung wert |
| Nur Datei-Sync ohne Weboberfläche | 2–3 Geräte, keine Freigaben | Syncthing ist viel leichter – aber keine Kalender/Web/Freigabe-Links |
cloud.seb4u.com, TLS (Transport Layer Security), .well-known-Redirects1 Server dimensionieren
| Nutzung | Server | Speicher |
|---|---|---|
| 1–2 Personen, Dateien + Kalender | CPX22 (2/4) | lokale Platte, ~40 GB (Gigabyte) reichen fangs |
| Haushalt (3–5), Fotos, gelegentlich Office | CPX32 (4/8) | Hetzner Volume (live vergrößerbar) oder S3 |
| + Collabora dauerhaft, viele Fotos | CPX42 (8/16) | S3-Objektspeicher als Primärspeicher („unbegrenzt“) |
php-fpm + MariaDB + Redis + (Collabora) laufen alle gleichzeitig. CPX22 mit
4 GB geht für Dateien, wird mit Collabora eng. Fotos-Vorschauen und der
Cron-Job fressen zusätzlich CPU (Central Processing Unit) – darum die neue AMD-Linie (cpx),
die bei Hetzner besser verfügbar ist als cx.
Grundserver nach der Mehrere-Dienste-Anleitung
(Traefik ist schon da). Nextcloud kommt als weiterer Stack unter /opt/stacks/nextcloud.
2 DNS, Traefik, .well-known
Das generische Compose-/Label-Muster (Netz proxy,
secure-headers, ein Router je Dienst) steht als Baustein in
Mehrere Dienste auf einem Server.
Hier nur der Nextcloud-Zusatz: die .well-known-Redirects und HSTS.
- Cloudflare-DNS:
cloudals A/AAAA (IPv6-Adress-Record) auf den Server (oder CNAME auf den Tunnel). - Traefik-Labels am Nextcloud-Service (Compose unten). Wichtig: die
.well-known-Weiterleitungen – sonst meckert der Admin-Bereich und CalDAV-Autoconfig scheitert.
labels:
- traefik.enable=true
- traefik.http.routers.nc.rule=Host(`cloud.seb4u.com`)
- traefik.http.routers.nc.entrypoints=websecure
- traefik.http.routers.nc.tls.certresolver=le
- traefik.http.routers.nc.middlewares=nc-wellknown,nc-headers
- traefik.http.services.nc.loadbalancer.server.port=80
# .well-known -> remote.php/dav
- traefik.http.middlewares.nc-wellknown.redirectregex.regex=^https://([^/]+)/\.well-known/(card|cal)dav
- traefik.http.middlewares.nc-wellknown.redirectregex.replacement=https://$${1}/remote.php/dav/
- traefik.http.middlewares.nc-wellknown.redirectregex.permanent=true
# HSTS
- traefik.http.middlewares.nc-headers.headers.stsSeconds=15552000
- traefik.http.middlewares.nc-headers.headers.stsIncludeSubdomains=true
3 Compose-Stack
services:
db:
image: mariadb:11.4
restart: unless-stopped
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
environment:
MARIADB_AUTO_UPGRADE: "1"
MYSQL_DATABASE: nextcloud
MYSQL_USER: nextcloud
MYSQL_PASSWORD_FILE: /run/secrets/db
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
volumes: [ db:/var/lib/mysql ]
secrets: [ db ]
networks: [ internal ]
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --save "" --maxmemory 256mb --maxmemory-policy allkeys-lru
networks: [ internal ]
app:
image: nextcloud:30-fpm-alpine
restart: unless-stopped
environment:
MYSQL_HOST: db
MYSQL_DATABASE: nextcloud
MYSQL_USER: nextcloud
MYSQL_PASSWORD_FILE: /run/secrets/db
REDIS_HOST: redis
NEXTCLOUD_TRUSTED_DOMAINS: cloud.seb4u.com
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD_FILE: /run/secrets/nc_admin
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: https://cloud.seb4u.com
PHP_MEMORY_LIMIT: 1G
PHP_UPLOAD_LIMIT: 10G
volumes:
- nc:/var/www/html
- data:/var/www/html/data
secrets: [ db, nc_admin ]
networks: [ internal ]
cron:
image: nextcloud:30-fpm-alpine
restart: unless-stopped
entrypoint: /cron.sh # <-- DER Cron-Job, siehe Abschnitt 5
volumes:
- nc:/var/www/html
- data:/var/www/html/data
networks: [ internal ]
depends_on: [ app ]
web:
image: nginx:1.27-alpine
restart: unless-stopped
volumes:
- nc:/var/www/html:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks: [ internal, web ]
depends_on: [ app ]
# hier die Traefik-Labels aus Abschnitt 2
volumes: { db: {}, nc: {}, data: {} }
networks:
web: { name: web, external: true }
internal: {}
secrets:
db: { file: ./db_password.txt }
nc_admin: { file: ./admin_password.txt }
nginx.conf für Nextcloud-fpm: die offizielle Vorlage aus dem
nextcloud/docker-Repo
(.examples/dockerfiles/fpm/nginx/nginx.conf) – sie enthält die
client_max_body_size, die fastcgi_*-Timeouts und die Regeln, die
Nextcloud im Sicherheits-Scan erwartet.
4 Erst-Einrichtung
cd /opt/stacks/nextcloud
openssl rand -base64 24 > db_password.txt
openssl rand -base64 24 > admin_password.txt # danach in den Passwortmanager!
docker compose up -d
docker compose logs -f app # "Initializing finished" abwarten
occ() { docker compose exec -u www-data app php occ "$@"; }
occ config:system:set default_phone_region --value DE
occ config:system:set maintenance_window_start --type integer --value 1 # 01:00 UTC fuer schwere Jobs
occ config:system:set memcache.local --value '\OC\Memcache\APCu'
occ config:system:set memcache.distributed --value '\OC\Memcache\Redis'
occ config:system:set memcache.locking --value '\OC\Memcache\Redis'
occ config:system:set redis host --value redis
occ config:system:set redis port --type integer --value 6379
occ db:add-missing-indices
occ maintenance:repair --include-expensive
occ status
Die erste Zeile ist der Schlüssel zum Rest – ohne sie müsste vor jedem Aufruf der ganze docker-Vorspann stehen:
| Konstrukt | Stelle | bewirkt |
|---|---|---|
occ() { docker compose exec -u www-data app php occ "$@"; } |
Zeile 1 | Eine Shell-Funktion – eine Abkürzung, die nur in dieser Konsolensitzung existiert. Alles danach liest sich, als wäre occ ein installiertes Programm, tatsächlich wird jedes Mal der lange Aufruf ausgeführt. Sie muss mitkopiert werden, sonst scheitern alle folgenden Zeilen mit occ: command not found. Nach dem Schließen des Fensters ist sie weg; für dauerhaft gehört sie in die ~/.bashrc. |
"$@" |
in der Funktion | Reicht alle übergebenen Argumente einzeln weiter – und zwar unter Beibehaltung ihrer Grenzen. Mit $* oder ohne Anführungszeichen würde ein Argument mit Leerzeichen in mehrere zerfallen; bei --value '\OC\Memcache\APCu' weiter unten macht genau das den Unterschied. |
docker compose exec -u www-data |
in der Funktion | Führt den Befehl als der Benutzer aus, dem die Nextcloud-Dateien gehören. Als root ausgeführt legt occ Dateien an, die dem Webserver anschließend nicht mehr gehören – ein Fehler, der erst Tage später als unerklärliche Rechteprobleme auffällt. app ist dabei der Dienstname aus der compose-Datei, nicht der Containername. |
--type integer |
zwei Aufrufe | occ speichert Werte sonst als Zeichenkette. Nextcloud erwartet bei maintenance_window_start und dem Redis-Port aber eine Zahl und ignoriert "1" stillschweigend. Ein Blick in config/config.php zeigt den Unterschied: '1' gegen 1. |
'\OC\Memcache\APCu' |
memcache | Die einfachen Anführungszeichen sind Pflicht: die Backslashes sind Teil des PHP-Klassennamens. In doppelten Anführungszeichen würde die Shell sie als Maskierungszeichen deuten und der Wert käme verstümmelt an. |
memcache.local vs. memcache.distributed vs. memcache.locking |
memcache | Drei verschiedene Aufgaben, deshalb drei Zeilen. local ist der Cache dieses Servers (APCu, im PHP-Prozess). distributed teilen sich mehrere Server – hier Redis. locking verhindert, dass zwei Zugriffe dieselbe Datei gleichzeitig ändern; ohne diese dritte Zeile treten bei parallelen Uploads sporadisch beschädigte Dateien auf. |
occ redis host --value redis |
Redis | redis ist hier der Dienstname aus der compose-Datei, den Docker im internen Netz als Hostnamen auflöst – kein DNS-Eintrag und keine IP nötig. |
occ db:add-missing-indices |
Aufräumen | Legt Datenbank-Indizes an, die neuere Nextcloud-Versionen erwarten, bei einem Update aber nicht automatisch ergänzen – die Sperre während des Anlegens könnte bei großen Installationen zu lange dauern. Genau darauf weist die Weboberfläche unter „Sicherheits- & Einrichtungswarnungen“ hin. |
maintenance:repair --include-expensive |
Aufräumen | Führt zusätzlich die Reparaturschritte aus, die länger dauern und deshalb bei jedem Update übersprungen werden. Bei einer frischen Installation sind sie in Sekunden durch – später mit vielen Dateien können daraus Minuten werden, in denen die Instanz nicht erreichbar ist. |
Nach dem ersten Login: scan.nextcloud.com
→ cloud.seb4u.com eingeben. Ziel:
Note A. Findet fehlende Header, .well-known-Probleme, veraltete
Versionen.
5 Der Cron-Job – die klassische Falle
Ohne einen echten Cron-Job läuft Nextcloud scheinbar, aber: Erinnerungen kommen nicht, Vorschaubilder fehlen, der Papierkorb läuft voll, die DB wächst, Foto-Erkennung passiert nie. Der Admin-Bereich zeigt eine gelbe Warnung.
- Der
cron-Container oben ruft/cron.shauf – das startetbusybox crond, das alle 5 Minutenphp -f cron.phpausführt. - In Nextcloud: → auf Cron stellen (nicht „AJAX“, nicht „Webcron“).
- Prüfen:
occ background:cronund im Admin-Bereich steht „Zuletzt ausgeführt: vor wenigen Minuten“.
docker compose exec -u www-data app php cron.php # manueller Lauf, muss ohne Fehler durch
docker compose logs cron --tail 20
Ohne dieses Fenster laufen die schweren wöchentlichen Jobs (Datei-Scans,
Aktivitäts-Aufräumen) irgendwann tagsüber und machen die Instanz zäh. Auf eine
ruhige Nachtstunde (UTC!) setzen – siehe occ-Befehl oben.
6 Wo die Dateien liegen
| Ziel | wie | gut / schlecht |
|---|---|---|
| Lokale Server-Platte | das data-Volume | schnell, einfach – aber begrenzt (Server-Disk) und beim Server-Neuaufbau mitzunehmen |
| Hetzner Volume | Volume anhängen, data darauf mounten | live vergrößerbar (bis 10 TB), überlebt den Server, ~0,044 €/GB/Mon |
| S3-Objektspeicher als Primärspeicher | objectstore in config.php – vor der Ersteinrichtung | „unbegrenzt“, günstig pro GB – aber jede Datei-Operation ist ein S3-Request, Metadaten bleiben in der DB |
| Storage Box als externer Speicher | SMB (Server Message Block)/WebDAV (Web Distributed Authoring and Versioning) mounten → App „Externe Speicher“ | gut für Kalt-Daten / große Medien, nicht für den Haupt-Sync-Pfad |
'objectstore' => [
'class' => '\\OC\\Files\\ObjectStore\\S3',
'arguments' => [
'bucket' => 'nextcloud',
'autocreate' => true,
'key' => getenv('OBJECTSTORE_S3_KEY'),
'secret' => getenv('OBJECTSTORE_S3_SECRET'),
'hostname' => 'fsn1.your-objectstorage.com', # Hetzner Object Storage
'port' => 443,
'use_ssl' => true,
'use_path_style' => true,
'region' => 'fsn1',
],
],
Von lokaler Platte auf S3 (oder umgekehrt) migriert man nicht einfach – es gibt Werkzeuge, aber sie sind fehleranfällig. Entscheide vorher. Im Zweifel: mit dem Volume anfangen, das wächst mit.
7 Redis & PHP-Tuning
- APCu lokal + Redis verteilt (im
occ-Block oben gesetzt): APCu für den schnellen Prozess-Cache, Redis für das File-Locking – das verhindert, dass zwei Sync-Clients dieselbe Datei zerschießen. PHP_MEMORY_LIMIT: 1G(im Compose) – darunter brechen große Foto-Alben und der ZIP-Download von Ordnern ab.PHP_UPLOAD_LIMIT+ nginxclient_max_body_sizemüssen beide groß sein, sonst scheitert der Upload großer Dateien am kleineren Wert.- php-fpm
pm.max_children: Faustregel(RAM_fuer_fpm / 80 MB). Zu hoch → OOM (Out Of Memory), zu niedrig → hängende Requests unter Last. EnvPHP_FPM_PM_MAX_CHILDRENim Image. - Nach Config-Änderungen
docker compose restart app webundocc maintenance:update:htaccess.
8 Office: Collabora
Dokumente im Browser bearbeiten. Zwei Wege:
- Eingebautes „Nextcloud Office“ (App Nextcloud Office + built-in CODE server): ein Klick, kein extra Container – aber der CODE-Server läuft im Nextcloud-Container und braucht dort ~1 GB extra. Für < ~10 Nutzer ok.
- Eigener Collabora-Container (empfohlen ab Haushaltsgröße):
collabora:
image: collabora/code:24.04
restart: unless-stopped
environment:
aliasgroup1: https://cloud.seb4u.com:443
extra_params: --o:ssl.enable=false --o:ssl.termination=true
networks: [ web ]
# Traefik: Host(`office.seb4u.com`) -> port 9980
In Nextcloud: App Nextcloud Office installieren →
→ „Eigener Server“ → https://office.seb4u.com.
Collabora braucht 1–2 GB RAM – das ist der Grund für
CPX42 statt CPX32, wenn Office dauerhaft laufen soll.
9 Clients & Sync
- Desktop: Nextcloud-Client (Windows/Mac/Linux) – Ordner-Sync, Virtual Files (nur bei Bedarf herunterladen).
- Mobil: Nextcloud-App (Foto-Auto-Upload!), plus separate Apps für Notes, Deck, Talk.
- Kalender/Kontakte iOS: Einstellungen → Kalender → Account →
CalDAV, Server
cloud.seb4u.com(dank.well-knownreicht die Domain), App-Passwort aus Nextcloud (). - Android: DAVx&sup5; für CalDAV/CardDAV (vCard Extensions to WebDAV).
- Immer App-Passwörter pro Gerät, nie das Hauptpasswort – so lässt sich ein verlorenes Gerät einzeln sperren.
10 Backup & Restore
Drei Teile: die Datenbank, das data-Verzeichnis
(bzw. der S3-Bucket) und config.php. Alle drei müssen
zueinander passen – darum kurz in den Wartungsmodus.
cd /opt/stacks/nextcloud
docker compose exec -T -u www-data app php occ maintenance:mode --on
docker compose exec -T db mariadb-dump --single-transaction nextcloud > dumps/nextcloud.sql
# restic sichert jetzt: dumps/, das data-Volume, config.php (siehe Backup-Anleitung)
docker compose exec -T -u www-data app php occ maintenance:mode --off
- Bei S3-Primärspeicher liegen die Datei-Inhalte im Bucket – den
separat versionieren (Bucket-Versioning +
restic/rcloneauf einen zweiten Bucket). Die DB bleibt trotzdem kritisch: ohne sie sind die S3-Objekte unentschlüsselbarer Datensalat. - Restore-Probe: DB einspielen,
datazurücklegen,config.phppassend,occ maintenance:data-fingerprint,occ files:scan --all,maintenance:mode --off. Login testen, eine Datei öffnen. – Details im Backup & Restore-Guide. - Nextcloud AIO bringt ein eigenes BorgBackup mit – wenn du AIO nutzt, das nehmen und trotzdem einmal einen Restore proben.
11 Betrieb & Updates
- Eine Hauptversion pro Schritt: 30 → 31 → 32, nie 30 → 32.
Image-Tag im Compose um eins hochsetzen,
docker compose up -d, das Image migriert beim Start (bzw.occ upgrade). Vorher Backup. occ status,occ app:update --all,occ integrity:check-core,occ maintenance:repairnach jedem Update.- Apps sparsam: jede installierte App ist Angriffsfläche und ein potenzielles Update-Hindernis. Nur was du wirklich nutzt.
- Monitoring: die
/ocs/v2.php/apps/serverinfo/api/v1/info(mit Token) liefert JSON (JavaScript Object Notation) für Uptime Kuma / Netdata, oder simpel ein HTTP-Keyword-Check auf die Login-Seite. - Log:
occ log:manage --level warning(nicht debug im Betrieb),docker compose logs app | grep -i error. - Benachrichtigungs-E-Mail: Port 25 ist bei Hetzner gesperrt – einen SMTP-Relay eintragen (Brevo/Mailgun Free-Tier oder eigener Mailserver).
+ Hetzner-spezifisch
- Volume für
data: in der Console anhängen, formatieren, nach/mnt/nc-datamounten (fstab!), im Composedatadarauf zeigen lassen. Vergrößern: Console → Volume → Größe → danachresize2fs. - Hetzner Object Storage (S3) für Primärspeicher: Buckets +
Zugangsschlüssel in der Console, Endpoint
<location>.your-objectstorage.com,use_path_style => true. - Storage Box als externer „Kalt“-Speicher: als SMB einbinden
oder in Nextcloud die App External storage mit WebDAV
(
https://<user>.your-storagebox.de). - Server-Neuaufbau: die Volumes (
db,nc,data) überleben den Snapshot-Zyklus nicht automatisch – entweder Hetzner-Volume nutzen (bleibt) oder die Volumes ins Backup und beim Aufbau zurück.
+ Kosten
Preise sind Richtwerte. Hetzner hat 2026 zweimal erhöht (zuletzt 15. Juni: CX/CAX +30–40 %, CPX/CCX über 100 %). Aktuelle Server-Preise, alle Nebenposten, ein Rechner und Beispielrechnungen stehen zentral in Kosten & Budget.
| Posten | €/Tag | €/Monat |
|---|---|---|
Server CPX32 (4 vCPU / 8 GB) – CX33/CAX21 ~8,5–10,5 falls frei | ~1,17 | ~35,5 |
| Primary-IP (IPv4) | ~0,02 | ~0,50 |
Hetzner Volume 100 GB (für data) | ~0,15 | ~4,40 |
| ODER Object Storage (S3), ~200 GB | ~0,04 | ~1–2 |
Collabora dauerhaft → CPX42 statt CPX32 | +~1,11 | +~34 |
| Backup (Restic → Storage Box, siehe eigene Anleitung) | ~0,13 | ~3,8 |
| Haushalt, ohne Office | ~0,60 | ~16–18 |
Vergleich: ein 2-TB-Cloud-Abo (Google One, iCloud+, Proton) liegt bei ~10 €/Monat – Nextcloud lohnt sich weniger über den Preis als über Kontrolle, Datenschutz und die Nebenfeatures (Kalender, Freigaben, Office, Apps).