Migrer un Nextcloud bare-metal vers Docker
Cet article explique comment passer d’un Nextcloud installé « à la main » (Debian, Apache + PHP 8.2, PostgreSQL local) à l’image Docker officielle avec Docker Compose, en gardant tout : comptes, fichiers, partages, calendriers, contacts, applications. Pas de réinstallation.
Il marche pour n’importe quel hôte Docker. Seul Nextcloud est concerné.
Situation de départ
Exemple réel :
- Nextcloud 34.0.5, PHP 8.2, PostgreSQL 15 sur la même machine ;
- données dans
/data/nextcloud(~200 Go), 4 utilisateurs ; - chiffrement côté serveur activé (clé maître) ;
- beaucoup d’applications : Memories, Recognize, Office intégré (richdocumentscode), Talk, Mail, Maps, Deck, Music…
Cible
nextcloud:34.0.5-apache, un servicecron(même image, entrypoint/cron.sh),redis:7.4-alpineetcollabora/code;- la même version que l’ancienne instance : on migre d’abord, on met à jour après ;
- une base PostgreSQL externe, plus récente (ici 17) ;
- deux dossiers sur l’hôte :
html→/var/www/htmletdata→/data/nextcloud. On garde le même chemin de données qu’avant, ce qui évite de toucher aux chemins enregistrés dans la base.
Exemple de docker-compose.yml (à adapter, les mots de passe sont à mettre dans un fichier .env) :
x-nextcloud-env: &nextcloud-env
POSTGRES_HOST: db.example.org
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${DB_PASSWORD}
REDIS_HOST: redis
PHP_MEMORY_LIMIT: 2G
PHP_UPLOAD_LIMIT: 16G
OVERWRITEPROTOCOL: https
services:
nextcloud:
image: nextcloud:34.0.5-apache
restart: unless-stopped
depends_on: [redis]
ports:
- "8080:80"
environment: *nextcloud-env
volumes:
- ./html:/var/www/html
- ./data:/data/nextcloud
cron:
image: nextcloud:34.0.5-apache
restart: unless-stopped
entrypoint: /cron.sh
depends_on: [redis]
environment: *nextcloud-env
volumes:
- ./html:/var/www/html
- ./data:/data/nextcloud
redis:
image: redis:7.4-alpine
restart: unless-stopped
collabora:
image: collabora/code
restart: unless-stopped
cap_add: [MKNOD]
ports:
- "9980:9980"
environment:
aliasgroup1: ${NEXTCLOUD_URL}
extra_params: --o:ssl.enable=false --o:ssl.termination=true
Le point important : cron reçoit exactement les mêmes variables que le service web (l’ancre YAML *nextcloud-env s’en charge). Voir plus bas pourquoi.
Le déroulé
1. Préparer l’ancienne instance
Si le disque cible est déjà chiffré, on désactive le chiffrement côté serveur :
sudo -u www-data php occ encryption:decrypt-all
sudo -u www-data php occ encryption:disable
sudo -u www-data php occ app:disable encryption
Attention, lisez la section « Chiffrement côté serveur » plus bas avant de lancer ça.
On désactive aussi les applications inutilisées :
sudo -u www-data php occ app:disable app_api
sudo -u www-data php occ app:disable recognize
2. Préparer la cible
Les dossiers html et data appartiennent à 33:33 (www-data dans l’image) :
mkdir -p html data
chown 33:33 html data
Côté PostgreSQL, on crée le rôle et la base :
CREATE ROLE nextcloud LOGIN PASSWORD '...';
CREATE DATABASE nextcloud OWNER nextcloud;
3. Pré-copie des données à chaud
C’est l’étape la plus longue (200 Go), mais elle se fait sans coupure de service :
rsync -aHAX --numeric-ids --info=progress2 /data/nextcloud/ nouvelhote:/chemin/data/
On la relancera à la bascule : seul le delta sera copié.
4. Premier démarrage de l’image
On démarre l’image une première fois pour qu’elle remplisse html avec le code de Nextcloud, sans remplir la page d’installation (on n’ouvre pas le site dans le navigateur) :
docker compose up -d nextcloud
docker compose logs -f nextcloud
Quand le code est copié, on arrête : docker compose stop.
5. Copier et adapter config.php
On copie le config.php de l’ancienne instance dans html/config/ et on l’adapte :
- garder
instanceid,secret,passwordsalt,version,datadirectory; - changer
dbhost, les identifiants de base ettrusted_proxies; - enlever
memcache.locking(Redis s’en charge via leredis.config.phpde l’image),logfileet les chemins de binaires (Memories).
6. Copier les applications non livrées
Les applications installées depuis l’App Store vivent dans apps/ sur l’ancienne instance, mais l’image Docker les attend dans custom_apps/. Voir plus bas pour la liste.
7. Bascule
-
Mode maintenance sur l’ancienne instance :
occ maintenance:mode --on. -
Dernier
rsyncavec--delete. -
Dump et restauration de la base :
pg_dump -Fc nextcloud > nextcloud.dump pg_restore --no-owner --role=nextcloud -d nextcloud nextcloud.dump -
Démarrage du conteneur, toujours en maintenance (
maintenance:modeest dans leconfig.phpcopié). -
occ maintenance:repair. -
Sortie de maintenance :
occ maintenance:mode --off. -
Bascule du reverse proxy ou du DNS vers le nouveau conteneur.
Tous les occ se lancent dans le conteneur :
docker compose exec -u www-data nextcloud php occ maintenance:repair
8. Après la migration
Quelques réglages, puis Collabora. Détails plus bas.
Les problèmes rencontrés
Chiffrement côté serveur
Après encryption:decrypt-all, il restait des fichiers chiffrés illisibles : environ 1 700 anciennes versions, des corbeilles, et un vrai fichier perdu. Solution : versions:cleanup, trashbin:cleanup, et accepter la perte.
Leçons :
- vérifier ce qui reste chiffré avant de couper le module ;
- garder les dossiers
files_encryptionjusqu’à validation complète.
Lister les applications « en plus » sans occ
Avec l’ancienne instance arrêtée, pas d’occ app:list. Mais une application « en plus » est simplement un dossier présent dans l’ancien apps/ et absent du apps/ de la nouvelle image :
OLD=/var/www/nextcloud/apps
NEW=html/apps
mkdir -p html/custom_apps
for app in $(ls "$OLD"); do
[ -d "$NEW/$app" ] && continue
[ "$app" = richdocumentscode ] && continue # remplacé par Collabora
rsync -a "$OLD/$app" html/custom_apps/
done
chown -R 33:33 html/custom_apps
On exclut ce qu’on remplace (richdocumentscode, remplacé par le conteneur Collabora).
Modifications de config.php « perdues »
Le conteneur lisait toujours dbhost => localhost, alors que j’avais modifié le fichier. Toujours vérifier ce que voit le conteneur plutôt que le fichier qu’on croit avoir modifié :
docker compose exec nextcloud grep dbhost config/config.php
Et vérifier que le bind mount pointe bien vers le bon dossier (docker compose config, docker inspect).
Le service cron oublié
Le service cron ne reçoit que les variables qu’on lui donne. Deux conséquences :
- sans
PHP_MEMORY_LIMIT, il plafonnait à 512 Mo et plantait sur les tâches Maps et métadonnées des photos. Et quand le cron plante, Nextcloud n’enregistre pas son passage (« dernière exécution il y a 4 heures ») ; - plus grave : sans
REDIS_HOST, leredis.config.phpde l’image ne s’active pas dans ce conteneur. Le web et le cron n’utilisent alors pas le même verrouillage de fichiers.
Donner au cron les mêmes variables que le service web.
Collabora
- Il lui faut son propre domaine : c’est le navigateur qui s’y connecte directement.
aliasgroup1= l’URL de Nextcloud (à passer en variable, iciNEXTCLOUD_URL).- Le SSL est terminé en amont :
--o:ssl.enable=false --o:ssl.termination=true. - Côté Nextcloud, on active la configuration :
occ richdocuments:activate-config.
Collabora peut tourner dans le même fichier Compose que Nextcloud (plus simple, mais les redémarrages sont communs) ou dans un fichier à part.