Nextcloud-Migration: Vom Shared Hosting zu Docker Compose

Wer seine Nextcloud von einem klassischen Hosting-Paket auf einen eigenen Server mit Docker umzieht, merkt schnell: Der Teufel steckt im Detail. In dieser Anleitung zeige ich dir, wie du den Umzug angeehen kannst, PHP-Limits sprengst und dein System mit Caddy absicherst.

📋 Voraussetzungen

  • Ein Linux-Server mit installiertem Docker und Docker Compose.
  • Ein Backup deiner alten Nextcloud (Dateien + Datenbank-Dump).
  • Eine Domain (z.B. share.deine-domain.de), die auf deinen Server zeigt.

Schritt 1: Die Verzeichnisstruktur

Wir trennen Code, Daten und Konfiguration sauber, um Updates und Backups zu vereinfachen.

mkdir -p /software/nextcloud/{nextcloud_install,config,data,db,backups}
  • nextcloud_install: Hier liegt der Programmcode.
  • config: Hier liegt die lebenswichtige config.php.
  • data: Deine Dateien und der tmp-Ordner für Uploads.

Schritt 2: Docker Compose Setup

Erstelle eine docker-compose.yml im Verzeichnis /software/nextcloud. Das Herzstück ist die Trennung von App, Datenbank und Redis (Caching).

services:
  db:
    image: mariadb:10.11
    container_name: nextcloud-db
    volumes:
      - ./db:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=dein_passwort
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud_user
      - MYSQL_PASSWORD=dein_db_passwort

  app:
    image: nextcloud:latest
    container_name: nextcloud-app
    depends_on:
      - db
      - redis
    volumes:
      - ./nextcloud_install:/var/www/html
      - ./config:/var/www/html/config
      - ./data:/var/www/html/data
    environment:
      - MYSQL_PASSWORD=dein_db_passwort
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud_user
      - MYSQL_HOST=db
      - REDIS_HOST=redis
      - PHP_MEMORY_LIMIT=1G
      - PHP_UPLOAD_LIMIT=16G

  redis:
    image: redis:alpine
    container_name: nextcloud-redis

  cron:
    image: nextcloud:latest
    container_name: nextcloud-cron
    volumes:
      - ./nextcloud_install:/var/www/html
      - ./config:/var/www/html/config
      - ./data:/var/www/html/data
    entrypoint: /cron.sh
    depends_on:
      - db
      - redis

Schritt 3: Der Umzug der Daten (Die "Pfad-Falle")

Nachdem du deinen Datenbank-Dump importiert und deine Dateien in den data-Ordner kopiert hast, kommt der wichtigste Teil: Pfade anpassen

Shared Hoster nutzen oft Pfade wie /var/www/vhosts/hosting123/....
Docker nutzt intern immer /var/www/html.

  1. Öffne deine config/config.php.
  2. Ändere datadirectory auf: 'datadirectory' => '/var/www/html/data',.
  3. Entferne alte apps_paths, falls vorhanden.

Schritt 4: Große Uploads ermöglichen (bis 20GB)

Standardmäßig blockieren viele Instanzen bei 512MB.
Wir brauchen drei Anpassungen:

4.1. Caddyfile (Proxy)

Dein Reverse Proxy muss den "Body" zulassen:

share.deine-domain.de {
# 1. Komprimierung für schnellere Ladezeiten
    encode zstd gzip

    # 2. Logging für Fehleranalyse
    log {
        output file /var/log/caddy/share.deine-domain.log
    }

    # 3. Sicherheits-Header (Wichtig für das Nextcloud-Security-Rating)
    header {
        # HSTS aktivieren (vorgeschriebene 6 Monate)
        Strict-Transport-Security "max-age=15552000; includeSubDomains; preload"
        
        # Clickjacking-Schutz
        X-Content-Type-Options nosniff
        X-Frame-Options SAMEORIGIN
        Referrer-Policy no-referrer-when-downgrade
        
        # Server-Tag verstecken (Sicherheit geht vor)
        -Server
    }

    # 4. Die "Schleuse" für große Uploads (z.B. Home Assistant Backups)
    request_body {
        max_size 20GB
    }

    # 5. Der Reverse Proxy zum Docker-Container
    reverse_proxy localhost:11000 {
        # Verhindert Pufferung, damit große Uploads nicht im RAM hängen
        flush_interval -1
        
        # Wichtige Header für die Erkennung der IP und Protokolle
        header_up Host {host}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}

        # Timeouts für extrem langsame/große Uploads
        transport http {
            read_timeout 3600s
            write_timeout 3600s
        }
    }
}

4.2. Die .user.ini

Erstelle im Ordner nextcloud_install eine Datei namens .user.ini, da Docker-Umgebungen diese für PHP-Limits nutzen:

upload_max_filesize=16G
post_max_size=16G
memory_limit=1G
max_execution_time=3600

4.3. Chunk-Size erhöhen

Tippe diesen Befehl im Terminal, um Nextcloud zu sagen, dass sie Dateien in größeren Stücken hochladen soll:

docker exec --user www-data nextcloud-app php occ config:app:set files max_chunk_size --value 104857600

Schritt 5: Automatisierung mit systemd-Timern

Wenn dein System kein crontab besitzt oder du eine robustere Lösung suchst, sind systemd-Timer die erste Wahl. Wir benötigen zwei Dinge pro Aufgabe: Einen .service (Was soll getan werden?) und einen .timer (Wann soll es getan werden?).

5.1 Der tägliche Backup-Job

Dieser Job sichert nachts um 03:00 Uhr die Datenbank und die Konfiguration.

1. Service-Datei erstellen: sudo nano /etc/systemd/system/nextcloud-backup.service

[Unit]
Description=Nextcloud Backup Service
After=docker.service

[Service]
Type=oneshot
ExecStart=/bin/bash /software/nextcloud/backup.sh
User=root

[Install]
WantedBy=multi-user.target

2. Timer-Datei erstellen: sudo nano /etc/systemd/system/nextcloud-backup.timer

[Unit]
Description=Trigger Nextcloud Backup daily at 3am

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

5.2 Der Nextcloud Hintergrund-Job (Cron)

Nextcloud muss alle 5 Minuten Aufgaben wie das Versenden von Benachrichtigungen oder das Aufräumen von Dateisperren erledigen. Ohne diesen Job wird dein System mit der Zeit langsam.

1. Service-Datei erstellen: sudo nano /etc/systemd/system/nextcloud-cron.service

[Unit]
Description=Trigger Nextcloud Cron.php inside Docker

[Service]
Type=oneshot
# Hier nutzen wir docker exec, um den Befehl im Container als www-data auszuführen
ExecStart=/usr/bin/docker exec --user 33 nextcloud-koralle-app php -f /var/www/html/cron.php

2. Timer-Datei erstellen: sudo nano /etc/systemd/system/nextcloud-cron.timer

[Unit]
Description=Run Nextcloud Cron every 5 minutes

[Timer]
OnBootSec=2min
OnUnitActiveSec=5min

[Install]
WantedBy=timers.target

5.3 Aktivierung und Überwachung

Nachdem die Dateien angelegt wurden, müssen sie dem System bekannt gemacht werden.

Einmalig laden und aktivieren:

# Systemd neu laden
sudo systemctl daemon-reload

# Timer aktivieren und sofort starten
sudo systemctl enable --now nextcloud-backup.timer
sudo systemctl enable --now nextcloud-cron.timer

Status und Fehlersuche: Einer der größten Vorteile von systemd ist das Journal. Du kannst genau sehen, ob und wann ein Job gelaufen ist:

  • Timer-Liste anzeigen: systemctl list-timers
  • Logs des Cron-Jobs einsehen: journalctl -u nextcloud-cron.service -f
  • Logs des Backups prüfen: journalctl -u nextcloud-backup.service

Wait, then there is more...

Schritt 6: Daten-Migration & Datenbank-Import

Bevor du docker compose up startest, müssen deine alten Daten an den richtigen Ort. Hier ist der präzise Ablauf:

6.1. Den Datenbank-Dump vorbereiten und importieren

Du hast von deinem alten Hoster eine .sql oder .sql.gz Datei. Der Trick bei Docker: Du musst die Daten in den laufenden Container schieben oder sie beim Starten automatisch einlesen lassen.

Variante A: Der manuelle Import (wenn der Container schon läuft)

  1. Kopiere den Dump auf deinen Server.

Schiebe den Dump per „Pipe“ direkt in den Container:

cat dein_backup.sql | docker exec -i nextcloud-db mariadb -u nextcloud_user -pdein_db_passwort nextcloud

Hinweis: Falls die Datei komprimiert ist, nutze zcat dein_backup.sql.gz | ....

Variante B: Der automatische Import (beim ersten Start) MariaDB-Container haben ein spezielles Verzeichnis: /docker-entrypoint-initdb.d/. Alles, was dort liegt, wird beim ersten Erstellen der Datenbank ausgeführt.

Ergänze in der docker-compose.yml beim db-Service:YAML

volumes:
  - ./db:/var/lib/mysql
  - ./dein_backup.sql:/docker-entrypoint-initdb.d/init.sql

6.2. Die Dateirechte (Permissions) – Der Stolperstein #1

Auf dem Shared Hosting gehörten deine Dateien einem User wie web123. Im Docker-Container ist es immer der User www-data mit der ID 33. Wenn die Rechte nicht stimmen, bekommst du einen "Internal Server Error".

Bevor du startest, korrigiere die Besitzer auf dem Host-System:

# Gehe in dein Nextcloud-Hauptverzeichnis
cd /software/nextcloud

# Setze den Besitzer für alle Ordner auf die ID 33 (www-data in Docker)
sudo chown -R 33:33 nextcloud_install config data apps

6.3. Der erste Login & App-Updates

Sobald die Container laufen (docker compose up -d), wird Nextcloud bemerken, dass die Datenbank von einem anderen Server kommt.

Apps aktualisieren: Da die alten App-Dateien evtl. nicht zur neuen Nextcloud-Version im Docker-Image passen:Bash

docker exec --user 33 nextcloud-app php occ upgrade

Indizes reparieren: Nach einem Import fehlen oft Optimierungen:Bash

docker exec --user 33 nextcloud-app php occ db:add-missing-indices

Wartungsmodus prüfen: Falls die Seite nicht lädt, schalte den Wartungsmodus aus:Bash

docker exec --user 33 nextcloud-app php occ maintenance:mode --off

Was tun bei Fehlern?

Wenn nach dem Import nur eine weiße Seite erscheint, ist der erste Blick immer in die Logs:

  • Docker-Ebene: docker compose logs -f app
  • Nextcloud-Ebene: tail -f /software/nextcloud/data/nextcloud.log

Oft sind es verwaiste Einträge in der oc_storages Tabelle der Datenbank, die noch auf den alten Pfad des Hosters zeigen. Mit occ files:scan --all zwingst du Nextcloud, die Dateistruktur neu einzulesen.

Fazit

Der Umzug zu Docker ist kein reiner Dateitransfer, sondern ein Konfigurations-Projekt. Der Lohn ist ein System, das exakt so skaliert, wie du es brauchst – egal ob für kleine Dokumente oder massive Home-Assistant-Backups.

Wenn Du direkt über neue Posts informiert werden, willst: einfach per Mail abbonieren