Sonntag, 10. August 2025

Nextcloud Migration zu All-in-One

Mein finales Migrationsziel soll eine Nextcloud AIO Installation sein. Dafür gibt es ein gutes Migrationsdokument (unbedingt zur Vorbereitung lesen). Auch hier steht wieder der explizite Hinweis, dass eine komplette Migration (Datenbank und Dateien) schwieriger ist und nicht unbedingt funktionieren muss. Es hat aber bei meiner Migration von einem auf den anderen Server auch funktioniert und ich hatte auch hiermit Erfolg. 

Anders als bei meiner Migration auf Docker Microservice wird hier nicht die komplette Installation sondern nur das Datenverzeichnis übernommen. Ganz nebenbei hatte ich dabei das Gefühl einer gewissen "Reinigung", hartnäckige Einträge im Protokoll waren nach dieser Migration erstmal verschwunden. 

Ich habe den Code aus dem Originaldokument etwas ergänzt und angepasst und noch ein paar Feinheiten aus anderen Quellen zusammengetragen.

Vorbereitung Quellsystem

Da mein Quellsystem mit Mariadb läuft, muss zunächst die Datenbank noch Postgres konvertiert werden. Ich habe dazu die docker-compose.yml einfach um einen postgres Container (explizit gleiche Version 17.5 wie im derzeitigen AIO System) mit der Bezeichnung db2 erweitert (Quelle: docker hub).

Ich habe diese Migration intern gemacht, mit einer Kopie des Produktivsystems. Um die Passwörter muss man sich damit keine Sorgen machen, man kann die Codezeilen so verwenden wie sie sind. Andernfalls muss man unbedingt gute Passwörter eintragen! Final läuft alles mit den gleichen Passwörtern wie im Quellsystem. 

cd ~/docker
cat << EOI >> docker-compose.yml
  db2:
    image: postgres:17.5
    restart: always
    # set shared memory limit when using docker compose
    shm_size: 128mb
    environment:
      POSTGRES_PASSWORD: example
EOI

docker container starten

docker compose up -d

Alles weitere entspricht diesem Dokument.

Datenbank erzeugen

export PG_USER="ncadmin"                  # This is a temporary user ...
export PG_PASSWORD="my-temporary-password"
export PG_DATABASE="nextcloud_db"

docker exec -u postgres -i docker-db2-1 psql <<END
CREATE USER $PG_USER WITH PASSWORD '$PG_PASSWORD' CREATEDB;
CREATE DATABASE $PG_DATABASE WITH OWNER $PG_USER TEMPLATE template0 ENCODING 'UTF8';
GRANT ALL PRIVILEGES ON DATABASE $PG_DATABASE TO $PG_USER;
GRANT ALL PRIVILEGES ON SCHEMA public TO $PG_USER;
END

Datenbank konvertieren

docker exec -u www-data -i docker-app-1 php \
occ db:convert-type --all-apps --password "$PG_PASSWORD" pgsql "$PG_USER" db2 "$PG_DATABASE"

Es folgen ein paar Ausgaben und eine Frage muss beantwortet werden. Die erfolgreiche Datenbankkonvertierung kann man im Webinterface der nextcloud unter settings/admin/serverinfo sehen.

Dump erzeugen

docker exec -u postgres docker-db2-1 pg_dump "$PG_DATABASE"  > ./database-dump.sql

Da nicht die komplette Installation sondern nur das Datenverzeichnis übernommen werden, müssen alle Apps die im Quellsystem installiert waren - vor der Migration - auf dem Zielsystem installiert werden.

Anstatt in der Web-UI zu suchen kann man eine Liste der installierten Apps von beiden Systemen  erzeugen, vergleichen und fehlende Apps installieren. Diesen Befehl für den Quellcontainer docker-app-1 und den Zielcontainer nextcloud-aio-nextcloud ausführen, bei mir auf separaten Host Systemen.

docker exec <nextcloud Container> php occ app:list

Jetzt wird das Zielsystem gestartet und falls noch nicht erfolgt, fertig installiert.

Zielsystem installieren

Ich betreibe die Nextcloud später hinter einem Reverse Proxy, das muss beim Start berücksichtigt werden (Quelle). Die Installation erfolgt einfach durch Start eines Mastercontainers, dieser orchestriert die restlichen Container selbst (dauert etwas). Entweder als Kommandozeile:

docker run -d \
--init \
--sig-proxy=false \
--name nextcloud-aio-mastercontainer \
--restart always \
--publish 8080:8080 \
--env APACHE_PORT=11000 \
--env APACHE_IP_BINDING=0.0.0.0 \
--env APACHE_ADDITIONAL_NETWORK="" \
--env SKIP_DOMAIN_VALIDATION=false \
--volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
--volume /var/run/docker.sock:/var/run/docker.sock:ro ghcr.io/nextcloud-releases/all-in-one:latest

Oder durch eine docker-compose.yml, hier als gut kommentiertes Grundgerüst verfügbar.

Hinweis: Das initiale Setup wird interaktiv durchgeführt: https://192.168.xx.yy:8080/setup . 

  1. Passphrase merken/aufschreiben, braucht bei Migration aber nur für erste Anmeldung.
  2. Neue Instanz, domain eintragen, muss jetzt per https:// erreichbar sein. Siehe weiter unten Testszenario.
  3. optionale Container auswählen, save changes
  4. submit Timezone
  5. download and start containers (dauert etwas, refresh machen)

Ab hier läuft das Zielsystem als "leeres" Grundsystem.

Die Installation/Deinstallation der Apps kann man auch mittels occ Befehl durchführen. Ich habe allerdings nicht herausgefunden, wie man an den app Namen einer nicht installierten und nicht deaktivierten App herankommt.

Ist die Installation der Apps abgeschlossen wird im AIO Interface ein backup gestartet und die Container werden nicht wieder gestartet! Backup Key merken/aufschreiben, der wird dauerhaft benötigt! Funktioniert das Backup, ist die AIO Instanz auch getestet.

Da ich auf zwei Systemen arbeite, muss ich die Daten vom Quellsystem zunächst kopieren. Ich brauche später nur den dump der Datenbank, den data und config Ordner. Auf den enthaltenen upgrade_instanceID Ordner könnte man eventuell auch verzichten, er enthält die letzten 3 automatischen Backups beim Upgrade.

mkdir sourceData && cd sourceData && mkdir DocRoot
scp root@192.168.xx.yy:docker/database-dump.sql .
scp -r root@192.168.xx.yy:docker/DocRoot/config DocRoot
scp -r root@192.168.xx.yy:docker/DocRoot/data DocRoot/

Im Dump muss der Pfad zum data Pfad angepasst werden.

sed -i "s|/var/www/html|/mnt/ncdata|" database-dump.sql

Mit Hilfe von temporären Containern werden die nächsten Schritte erledigt. 

Nextcloud Daten in der AIO Instanz austauschen

Der Dump wird im Container so platziert, dass er beim nächsten Start in die Datenbank importiert wird und die alten Daten ersetzt.

docker run --rm --volume nextcloud_aio_database_dump:/mnt/data:rw alpine rm /mnt/data/database-dump.sql
docker cp database-dump.sql nextcloud-aio-database:/mnt/data/
docker run --rm --volume nextcloud_aio_database_dump:/mnt/data:rw alpine chmod 777 /mnt/data/database-dump.sql
docker run --rm --volume nextcloud_aio_database_dump:/mnt/data:rw alpine rm /mnt/data/initial-cleanup-done

Das data Verzeichnis im Zielsystem wird gelöscht und durch die Daten vom Quellsystem ersetzt.

docker run --rm --volume nextcloud_aio_nextcloud_data:/mnt/ncdata:rw alpine sh -c "rm -rf /mnt/ncdata/*"
docker cp --follow-link DocRoot/data/. nextcloud-aio-nextcloud:/mnt/ncdata/

Berechtigungen setzen

docker run --rm --volume nextcloud_aio_nextcloud_data:/mnt/ncdata:rw alpine chown -R 33:0 /mnt/ncdata/
docker run --rm --volume nextcloud_aio_nextcloud_data:/mnt/ncdata:rw alpine chmod -R 750 /mnt/ncdata/

Config der AIO Instanz an die alte Nextcloud Instanz anpassen

Werte aus der alten config lesen ( Hinweis: \047string matched auf 'string, sonst werden u.U. zwei Einträge gefunden )

passwordsalt=$(awk -F"'" '/\047passwordsalt/{print $4}' DocRoot/config/config.php)
secret=$(awk -F"'" '/\047secret/{print $4}' DocRoot/config/config.php)
instanceid=$(awk -F"'" '/\047instanceid/{print $4}' DocRoot/config/config.php)
echo -e "Aus der config wurde gelesen \npasswordsalt \t$passwordsalt \nsecret \t$secret \ninstanceid \t$instanceid "

und in die neue config schreiben.

docker run --rm --volume nextcloud_aio_nextcloud:/var/www/html:rw alpine \
sed -i "s/'passwordsalt' => '.*'/'passwordsalt' => '$passwordsalt'/;\
s/'secret' => '.*'/'secret' => '$secret'/;\
s/'instanceid' => '.*'/'instanceid' => '$instanceid'/" /var/www/html/config/config.php

Jetzt die Container im AIO Interface wieder starten, beim Start wird die Migration durch den automatischen Import der Datenbank beendet.

Jetzt wird die migrierte Nextcloud unter der beim Setup eingetragenen url erreichbar sein. Die einzige Warnmeldung die komme sollte, kann durch setzen der Telefon Region behoben werden:

docker exec -u www-data -it nextcloud-aio-nextcloud php occ config:system:set default_phone_region --value="DE"


Testszenario

Will man die Migration ausgiebig testen, kann man mit einer temporären Domain arbeiten. Ist der Test abgeschlossen wird die Domain in der Konfiguration der AIO Instanz geändert und der DNS Eintrag für die produktive Domain geändert.

Zuerst im AIO Interface Stop containers! Die configuration.json im Master Container anpassen

docker exec -it nextcloud-aio-mastercontainer \
sed -i 's|"domain": "old.domain.tld"|"domain": "new.domain.tld"|' /mnt/docker-aio-config/data/configuration.json

Beim Start wird alles weitere erledigt, der Master Container passt die Umgebung an.

Von vorn beginnen

Hat man sich irgendwie in der Konfiguration vertan, kann man die Umgebung der AIO Instance komplett löschen und neu beginnen. Einen funktionierendes Verfahren habe ich hier gefunden. 

Will man dazu vorher die Daten der Nextcloud retten, kopiert man diese einfach "beiseite". AIO erzeugt beim shutdown selbständig einen Dump der Postgres Datenbank.

Anmerkung: Bei mir ging das Backup in der AIO Instanz nicht, weil der LXC Container fuse nicht aktiviert hatte. Deswegen habe ich das Verfahren mit Kopie der Daten probiert. 

mkdir data && cd data
mkdir -p DocRoot/data
docker cp nextcloud-aio-database:/mnt/data/database-dump.sql .
docker cp --follow-link nextcloud-aio-nextcloud:/mnt/ncdata/. DocRoot/data/

Diese Daten kann man analog dem oben genannten Verfahren (Abschnitt: Nextcloud Daten in der AIO Instanz austauschen) nach dem Reset der Instance wieder einfügen.

Reset der AIO Instance.

name=nextcloud
docker stop $(docker ps --filter "name=^${name}-" -q)
docker rm $(docker ps --filter "name=^${name}-" --filter status=exited -q)
docker volume rm $(docker volume ls --filter "name=^${name}_" -q)
docker network rm $(docker network ls --filter "name=^${name}-" -q)

Und bei Bedarf die angelegten Datenpfade löschen.

rm -RI ~/sourceData


ToDo

Aus den Quelldaten könnte man alte update Ordner (data/updater-instanceID) noch ausschließen.

Code


Keine Kommentare:

Kommentar veröffentlichen