ownCloud 10 en Docker : migration de 568 Go avec rsync progressif et zéro perte de données
ownCloud 10 en Docker — migration {{TAILLE_DONNEES}} avec rsync progressif
Septième étape : migrer ownCloud 10.16 ({{TAILLE_DONNEES}} de données utilisateur, PostgreSQL)
depuis une installation standalone VestaCP vers Docker Compose sur le serveur centralisé,
avec transfert progressif rsync et reverse proxy MyVestaCP.
Paramètres à personnaliser
Remplacez chaque {{VARIABLE}} par vos propres valeurs avant d’utiliser ce guide.
| Variable | Description | Exemple |
|---|---|---|
{{IP_ANCIEN}} |
IP du serveur source | 203.0.113.10 |
{{IP_NOUVEAU}} |
IP du serveur cible | 198.51.100.20 |
{{HOSTNAME_ANCIEN}} |
Hostname de l’ancien serveur | ns1234567 |
{{HOSTNAME_NOUVEAU}} |
Hostname du nouveau serveur | ns7654321 |
{{PORT_SSH}} |
Port SSH pour rsync | 22222 |
{{DOMAINE_OWNCLOUD}} |
Domaine ownCloud | owncloud.exemple.com |
{{USER_VESTA_OC}} |
Utilisateur VestaCP (ancien serveur) | myocuser |
{{USER_VESTA}} |
Utilisateur MyVestaCP (nouveau serveur) | admin |
{{DB_USER}} |
Utilisateur et base PostgreSQL | oc_user |
{{DB_PASSWORD}} |
Mot de passe PostgreSQL | S3cur3P@ss! |
{{ADMIN_TEMP_PASSWORD}} |
Mot de passe admin temporaire | TempP@ss123! |
{{OC_INSTANCEID}} |
instanceid ownCloud | abc123def456 |
{{OC_PASSWORDSALT}} |
passwordsalt ownCloud | randomSaltValue123... |
{{OC_SECRET}} |
secret ownCloud | longSecretKey456... |
{{SMTP_HOST}} |
Serveur SMTP | smtp.provider.com |
{{SMTP_DOMAIN}} |
Domaine expéditeur SMTP | exemple.com |
{{SMTP_PASSWORD}} |
Mot de passe SMTP | SmtpP@ss! |
{{OC_USER}} |
Utilisateur ownCloud (cron cleanup) | mon-utilisateur |
{{TAILLE_DONNEES}} |
Taille des données utilisateur | 100 Go |
Contexte
L’instance ownCloud 10.16.0 Community Edition tourne actuellement sur l’ancien serveur dédié
({{IP_ANCIEN}}, {{HOSTNAME_ANCIEN}}) géré par VestaCP, sous le chemin
/home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/.
Elle utilise PostgreSQL (pas MySQL), Redis pour le file locking,
et stocke {{TAILLE_DONNEES}} de fichiers utilisateur.
On migre vers Docker Compose sur le nouveau serveur ({{IP_NOUVEAU}}, {{HOSTNAME_NOUVEAU}}, SSH port {{PORT_SSH}}),
où tournent déjà Odoo 17, Odoo 8 et Akeneo PIM.
Architecture cible : 4 conteneurs —
owncloud/server:10.16 (port 8490),
postgres:13,
redis:7-alpine,
docker:27-cli (crons custom).
Les données utilisateur ({{TAILLE_DONNEES}}) sont en bind mount direct sur le disque
pour éviter la surcharge des volumes Docker sur HDD.
Stratégie rsync 2 passes : avec {{TAILLE_DONNEES}} de données sur HDD,
un transfert complet prend plusieurs heures. On fait un premier rsync pendant
que l’ancien serveur est encore en production (aucune interruption de service),
puis un second rsync rapide (delta seulement) après avoir activé le mode maintenance.
Résultat : fenêtre de maintenance réduite à ~30 minutes au lieu de plusieurs heures.
1) Collecter les informations sur l’ancien serveur
Avant de commencer, on récupère les informations clés sur l’ancien serveur.
1.1 Vérifier la version PostgreSQL
|
1 |
pg_lsclusters |
La version PostgreSQL détermine quelle image Docker utiliser.
Prendre une version égale ou supérieure (ex : si le serveur a PostgreSQL 11, utiliser postgres:13).
1.2 Relever les valeurs critiques de config.php
|
1 |
cat /home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/config/config.php |
Les trois valeurs obligatoires à préserver :
instanceid— identifiant unique de l’instance (utilisé par les clients de synchronisation)passwordsalt— sel pour le hachage des mots de passe (sans lui, aucun utilisateur ne peut se connecter)secret— clé de chiffrement pour les données sensibles
Ces trois valeurs sont irremplaçables. Si elles ne correspondent pas
entre la base de données importée et le config.php du nouveau serveur,
les utilisateurs ne pourront plus se connecter et les données chiffrées seront inaccessibles.
Notez-les précieusement.
1.3 Vérifier la taille des données et les apps marketplace
|
1 2 3 4 5 6 7 8 |
# Taille du data directory du -sh /home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/data/ # Apps marketplace (external) ls /home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/apps-external/ # Taille de la base PostgreSQL sudo -u postgres psql -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) FROM pg_database WHERE datname = '{{DB_USER}}';" |
Résumé de notre installation :
- Version : ownCloud 10.16.0 Community Edition
- Base de données : PostgreSQL 13, base
{{DB_USER}}, utilisateur{{DB_USER}}(3,8 Go) - Données utilisateur : {{TAILLE_DONNEES}} dans
/home/{{USER_VESTA_OC}}/.../public_html/data/ - Apps marketplace (apps-external) :
files_texteditor,gallery,twofactor_totp - Cache : APCu (local) + Redis (file locking)
- SMTP :
{{SMTP_HOST}}port 465 (SSL) - Domaine :
{{DOMAINE_OWNCLOUD}}
Phase 1 : Préparation (ancien serveur en production)
Toutes les étapes suivantes se font sans interrompre le service
sur l’ancien serveur.
2) Créer le domaine dans MyVestaCP
- Se connecter à MyVestaCP sur
https://{{IP_NOUVEAU}}:8083 - Aller dans WEB → Add Web Domain
- Domaine :
{{DOMAINE_OWNCLOUD}} - Cocher DNS Support
- Ne pas cocher « Enable SSL » pour l’instant
DNS Support doit être coché même si le DNS est géré ailleurs (OVH, Cloudflare, etc.).
Sans cette case, MyVestaCP ne crée pas la zone DNS locale et le template Nginx
ne résout pas correctement le domaine en interne.
3) Créer les templates Nginx
On crée deux templates pour que MyVestaCP route le trafic vers le conteneur ownCloud
sur le port 8490. Pas de WebSocket ni de longpolling — juste du proxy HTTP classique.
3.1 Template HTTP
|
1 |
nano /usr/local/vesta/data/templates/web/nginx/owncloud-8490.tpl |
Contenu :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
server { listen %ip%:%web_port%; server_name %domain_idn% %alias_idn%; client_max_body_size 16G; location / { proxy_pass http://127.0.0.1:8490; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 600s; proxy_send_timeout 600s; proxy_connect_timeout 75s; proxy_buffering off; proxy_request_buffering off; } } |
3.2 Template HTTPS
|
1 |
nano /usr/local/vesta/data/templates/web/nginx/owncloud-8490.stpl |
Contenu :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
server { listen %ip%:%web_ssl_port% ssl; server_name %domain_idn% %alias_idn%; ssl_certificate %ssl_pem%; ssl_certificate_key %ssl_key%; ssl_stapling on; ssl_stapling_verify on; client_max_body_size 16G; location / { proxy_pass http://127.0.0.1:8490; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_read_timeout 600s; proxy_send_timeout 600s; proxy_connect_timeout 75s; proxy_buffering off; proxy_request_buffering off; } } |
Points importants :
client_max_body_size 16G— ownCloud supporte l’upload de gros fichiers via chunking,
mais la limite Nginx doit être suffisamment haute pour ne pas bloquer les chunksproxy_buffering offetproxy_request_buffering off— empêchent Nginx
de mettre en cache les uploads sur le disque (critique sur HDD pour éviter la double écriture)proxy_read_timeout 600s— les opérations sur de gros fichiers peuvent prendre du temps
3.3 Appliquer le template
Dans MyVestaCP → WEB → cliquer sur {{DOMAINE_OWNCLOUD}} →
Edit → champ Nginx Template →
sélectionner owncloud-8490 → Save.
3.4 Vérifier les ports générés dans la config Nginx
Après avoir appliqué le template, MyVestaCP génère les fichiers de configuration
Nginx réels. Vérifier impérativement les ports :
|
1 2 |
head -3 /home/{{USER_VESTA}}/conf/web/{{DOMAINE_OWNCLOUD}}.nginx.conf head -3 /home/{{USER_VESTA}}/conf/web/{{DOMAINE_OWNCLOUD}}.nginx.ssl.conf |
Les fichiers doivent contenir listen {{IP_NOUVEAU}}:80 (HTTP)
et listen {{IP_NOUVEAU}}:443 ssl (HTTPS).
Piège fréquent : ports 8080/8443 au lieu de 80/443.
MyVestaCP utilise les variables %web_port% et %web_ssl_port%
dans les templates. Sur certaines configurations, ces variables résolvent vers
8080 et 8443 — qui sont les ports du backend Apache,
pas du frontend Nginx. Si Nginx tente d’écouter sur 8080/8443, il échoue avec
bind() failed (98: Address already in use) car Apache occupe déjà ces ports.
Correction : si les fichiers contiennent 8080 ou 8443, les corriger :
|
1 2 3 4 5 |
sed -i 's/8080/80/' /home/{{USER_VESTA}}/conf/web/{{DOMAINE_OWNCLOUD}}.nginx.conf sed -i 's/8443 ssl/443 ssl/' /home/{{USER_VESTA}}/conf/web/{{DOMAINE_OWNCLOUD}}.nginx.ssl.conf # Vérifier et recharger Nginx nginx -t && systemctl restart nginx |
4) Créer la structure des répertoires Docker
|
1 2 3 4 5 |
mkdir -p /home/docker/owncloud/data/files mkdir -p /home/docker/owncloud/data/config mkdir -p /home/docker/owncloud/data/apps mkdir -p /home/docker/owncloud/backup mkdir -p /home/docker/owncloud/config |
Docker Compose v2 : si Docker Compose v2 est déjà installé (vérifier avec
docker compose version), pas besoin de le réinstaller.
5) Créer les fichiers de configuration
5.1 Fichier docker-compose.yml
|
1 |
nano /home/docker/owncloud/docker-compose.yml |
Contenu :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
services: owncloud: image: owncloud/server:10.16 container_name: owncloud-server restart: always ports: - "127.0.0.1:8490:8080" depends_on: - db - redis environment: OWNCLOUD_DOMAIN: {{DOMAINE_OWNCLOUD}} OWNCLOUD_TRUSTED_DOMAINS: {{DOMAINE_OWNCLOUD}} OWNCLOUD_DB_TYPE: pgsql OWNCLOUD_DB_NAME: {{DB_USER}} OWNCLOUD_DB_USERNAME: {{DB_USER}} OWNCLOUD_DB_PASSWORD: {{DB_PASSWORD}} OWNCLOUD_DB_HOST: db OWNCLOUD_ADMIN_USERNAME: admin OWNCLOUD_ADMIN_PASSWORD: {{ADMIN_TEMP_PASSWORD}} OWNCLOUD_REDIS_ENABLED: "true" OWNCLOUD_REDIS_HOST: redis OWNCLOUD_SKIP_CHOWN: "true" OWNCLOUD_SKIP_CHMOD: "true" OWNCLOUD_OVERWRITE_PROTOCOL: https OWNCLOUD_OVERWRITE_CLI_URL: https://{{DOMAINE_OWNCLOUD}} volumes: - ./data:/mnt/data healthcheck: test: ["CMD", "/usr/bin/healthcheck"] interval: 30s timeout: 10s retries: 5 start_period: 600s networks: - owncloud-network db: image: postgres:13 container_name: owncloud-db restart: always environment: POSTGRES_USER: {{DB_USER}} POSTGRES_PASSWORD: {{DB_PASSWORD}} POSTGRES_DB: {{DB_USER}} volumes: - owncloud-db-data:/var/lib/postgresql/data networks: - owncloud-network redis: image: redis:7-alpine container_name: owncloud-redis restart: always command: ["--databases", "1"] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 volumes: - owncloud-redis-data:/data networks: - owncloud-network cron: image: docker:27-cli container_name: owncloud-cron restart: always volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./config/crontab:/crontab:ro entrypoint: ["/bin/sh", "-c", "cp /crontab /var/spool/cron/crontabs/root && chmod 0600 /var/spool/cron/crontabs/root && crond -f"] depends_on: - owncloud networks: - owncloud-network volumes: owncloud-db-data: owncloud-redis-data: networks: owncloud-network: name: owncloud-network |
Points clés :
owncloud/server:10.16— image officielle ownCloud (Ubuntu, Apache + PHP intégrés, port 8080)postgres:13— on garde PostgreSQL comme sur l’ancien serveur
(convertir de PostgreSQL vers MariaDB serait risqué et inutile)redis:7-alpine— pour le file locking et le cache (comme sur l’ancien serveur)docker:27-cli— conteneur léger (Alpine avec client Docker) pour exécuter les crons custom
(versions:cleanup, files:scan) en appelantdocker execsur le conteneur ownCloud./data:/mnt/data— bind mount pour les {{TAILLE_DONNEES}} de données
(même performance I/O qu’un accès natif, pas de surcharge Docker sur HDD)OWNCLOUD_SKIP_CHOWN: "true"etOWNCLOUD_SKIP_CHMOD: "true"—
critique, sans cela le conteneur exécute unchown -Rrécursif
sur les {{TAILLE_DONNEES}} à chaque démarrage, ce qui prendrait des heures sur HDDstart_period: 600s— 10 minutes de grâce pour le healthcheck
(le premier démarrage après migration peut être long)
OWNCLOUD_ADMIN_USERNAME / OWNCLOUD_ADMIN_PASSWORD :
ces valeurs ne sont utilisées que lors de la première initialisation (installation vierge).
Après l’import de la base de données, ce sont les comptes de l’ancienne base qui s’appliquent.
On met des valeurs temporaires ici car elles seront ignorées.
OWNCLOUD_SKIP_CHOWN : en activant cette option, on prend la responsabilité
de gérer les permissions nous-mêmes. Tous les fichiers dans /mnt/data doivent appartenir
à www-data (UID 33, GID 33).
L’image owncloud/server est basée sur Ubuntu — le UID 33 correspond à www-data.
5.2 Fichier crontab pour les crons custom
Créer un fichier de crontab pour les tâches de maintenance custom ownCloud
(nettoyage des anciennes versions, scan de fichiers). Ces crons s’exécutent dans
le conteneur owncloud-cron et appellent le conteneur principal via docker exec.
|
1 |
nano /home/docker/owncloud/config/crontab |
Contenu :
|
1 2 3 4 5 6 7 8 9 10 |
# ownCloud - Crons custom conteneurisés # Les commandes s'exécutent dans le conteneur owncloud-server via docker exec # Nettoyage des anciennes versions pour {{OC_USER}} (tous les jours à 1h30) 30 1 * * * docker exec -u www-data owncloud-server php /var/www/owncloud/occ versions:cleanup {{OC_USER}} > /dev/null 2>&1 # Scan complet de tous les fichiers (dimanche à minuit) 0 0 * * 0 docker exec -u www-data owncloud-server php /var/www/owncloud/occ files:scan --all --verbose > /dev/null 2>&1 |
Cron system:cron déjà intégré :
l’image owncloud/server:10.16 inclut un cron interne qui exécute
occ system:cron toutes les 15 minutes (tâches de fond, nettoyage de sessions, etc.).
Pas besoin de l’ajouter ici — seuls les crons custom spécifiques doivent être définis.
Ligne vide obligatoire à la fin : le crond de BusyBox (Alpine Linux)
exige que le fichier crontab se termine par une ligne vide. Sans cela, la dernière
entrée cron n’est pas prise en compte. Vérifiez qu’il y a bien une ligne vide après
la dernière ligne de cron.
6) Lancer le rsync initial (en arrière-plan)
Cette étape est la plus longue (plusieurs heures pour {{TAILLE_DONNEES}}).
On la lance pendant que l’ancien serveur est encore en production —
les utilisateurs continuent à travailler normalement.
6.1 Préparer l’accès SSH
Depuis le nouveau serveur, vérifier qu’on peut se connecter à l’ancien sans mot de passe :
|
1 2 3 4 5 |
# Générer une clé SSH si pas encore fait ssh-keygen -t ed25519 -C "migration-owncloud" # Copier la clé sur l'ancien serveur (port SSH personnalisé) ssh-copy-id -p {{PORT_SSH}} root@{{IP_ANCIEN}} |
6.2 Lancer le premier rsync
Depuis le nouveau serveur :
|
1 2 3 4 5 6 7 8 9 10 |
# Lancer dans un screen ou tmux pour ne pas perdre le transfert si la session SSH se coupe screen -S rsync-owncloud # Rsync initial : copie complète des données utilisateur rsync -avt --progress -e "ssh -p {{PORT_SSH}}" \ root@{{IP_ANCIEN}}:/home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/data/ \ /home/docker/owncloud/data/files/ # Détacher le screen : Ctrl+A puis D # Rattacher plus tard : screen -r rsync-owncloud |
Le flag -t est critique : il préserve les timestamps
des fichiers. Sans ce flag, tous les fichiers auront la date du transfert, et les clients
de synchronisation ownCloud considéreront que chaque fichier a été modifié,
déclenchant un re-téléchargement complet des {{TAILLE_DONNEES}} sur tous les postes clients.
Estimation de durée : entre serveurs OVH (réseau interne ~1 Gbps),
{{TAILLE_DONNEES}} prennent environ 1h30 à 3h.
La vitesse réelle dépend des HDD (lectures aléatoires sur beaucoup de petits fichiers = plus lent).
Pour suivre la progression, rattacher le screen.
6.3 Transférer les apps marketplace
Le répertoire apps-external/ contient 3 apps marketplace :
files_texteditor, gallery et twofactor_totp.
Les transférer :
|
1 2 3 |
rsync -avt --progress -e "ssh -p {{PORT_SSH}}" \ root@{{IP_ANCIEN}}:/home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/apps-external/ \ /home/docker/owncloud/data/apps/ |
Mapping des répertoires : sur l’ancien serveur, les apps marketplace sont dans
apps-external/. Dans le conteneur Docker, elles vont dans /mnt/data/apps/
(bind mounté vers /home/docker/owncloud/data/apps/ sur l’hôte).
Les apps core (apps/) sont déjà incluses dans l’image Docker.
Phase 2 : Basculement (fenêtre de maintenance)
À partir d’ici, le service ownCloud est interrompu sur l’ancien serveur.
Objectif : terminer en ~30 minutes.
7) Activer le mode maintenance sur l’ancien serveur
|
1 2 3 4 5 6 |
# Vérifier quel utilisateur possède les fichiers ownCloud ls -la /home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/config/config.php # Activer le mode maintenance (adapter l'utilisateur si nécessaire) cd /home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/ sudo -u {{USER_VESTA_OC}} php occ maintenance:mode --on |
Quel utilisateur pour occ ?
Sur VestaCP, chaque domaine a son propre utilisateur système. Ici c’est {{USER_VESTA_OC}}
(propriétaire des fichiers). Si la commande échoue avec Permission denied,
essayer avec sudo -u www-data à la place.
Vérifier :
|
1 |
sudo -u {{USER_VESTA_OC}} php occ maintenance:mode |
Doit afficher : Maintenance mode is enabled.
Attendre 5 minutes après l’activation pour que les clients de synchronisation
détectent le mode maintenance et arrêtent les uploads. Sinon, des fichiers pourraient
être modifiés entre le dump de la base et le rsync final, créant des incohérences.
8) Exporter la base PostgreSQL
|
1 2 |
# Sur l'ancien serveur sudo -u postgres pg_dump --no-owner --no-acl {{DB_USER}} > /tmp/owncloud-dump.sql |
Vérifier la taille du dump :
|
1 |
ls -lh /tmp/owncloud-dump.sql |
--no-owner --no-acl : ces flags suppriment les commandes
ALTER OWNER et GRANT/REVOKE du dump. Cela évite les warnings
liés au fait que l’ancien rôle PostgreSQL (créé par VestaCP) n’existe pas dans le conteneur Docker.
Toutes les tables seront créées sous le rôle {{DB_USER}} défini dans le docker-compose.
9) Rsync final + transfert du dump
Depuis le nouveau serveur, on lance le rsync final qui ne transfère
que les fichiers modifiés depuis le premier rsync (delta) :
|
1 2 3 4 5 6 7 8 9 |
# Rsync final (delta uniquement, très rapide) rsync -avt --delete --progress -e "ssh -p {{PORT_SSH}}" \ root@{{IP_ANCIEN}}:/home/{{USER_VESTA_OC}}/web/{{DOMAINE_OWNCLOUD}}/public_html/data/ \ /home/docker/owncloud/data/files/ # Transférer le dump PostgreSQL rsync -avt --progress -e "ssh -p {{PORT_SSH}}" \ root@{{IP_ANCIEN}}:/tmp/owncloud-dump.sql \ /home/docker/owncloud/backup/ |
--delete : supprime sur le nouveau serveur les fichiers qui ont été
supprimés sur l’ancien entre le premier et le second rsync.
Cela garantit une copie exacte.
10) Démarrer PostgreSQL et importer la base
10.1 Démarrer uniquement PostgreSQL et Redis
|
1 2 |
cd /home/docker/owncloud/ docker compose up -d db redis |
Attendre que PostgreSQL soit prêt :
|
1 |
docker compose logs -f db |
Attendre le message database system is ready to accept connections, puis Ctrl+C.
10.2 Importer le dump
|
1 |
docker compose exec -T db psql -U {{DB_USER}} -d {{DB_USER}} < /home/docker/owncloud/backup/owncloud-dump.sql |
Le flag -T désactive l’allocation de pseudo-TTY,
nécessaire pour la redirection stdin (<) vers le conteneur.
Doublons possibles : si le conteneur PostgreSQL a déjà créé des tables vides
pour la base {{DB_USER}}, le dump peut échouer avec relation already exists.
Dans ce cas, vider d'abord la base :
|
1 2 3 4 |
# Seulement si l'import échoue avec des erreurs de doublons : docker compose exec db psql -U {{DB_USER}} -d postgres -c "DROP DATABASE {{DB_USER}};" docker compose exec db psql -U {{DB_USER}} -d postgres -c "CREATE DATABASE {{DB_USER}} OWNER {{DB_USER}};" # Puis relancer l'import |
10.3 Vérifier l'import
|
1 2 3 4 5 |
# Compter les tables ownCloud (préfixe oc_) docker compose exec db psql -U {{DB_USER}} -d {{DB_USER}} -c "SELECT count(*) FROM information_schema.tables WHERE table_name LIKE 'oc_%';" # Vérifier les utilisateurs docker compose exec db psql -U {{DB_USER}} -d {{DB_USER}} -c "SELECT uid, displayname FROM oc_users;" |
11) Corriger les permissions du volume de données
Les fichiers transférés par rsync appartiennent à root (ou à l'ancien utilisateur {{USER_VESTA_OC}}).
Le conteneur Docker s'exécute en tant que www-data (UID 33).
|
1 2 |
# Corriger le propriétaire de tout le volume de données chown -R 33:33 /home/docker/owncloud/data/ |
Cette commande prend du temps sur {{TAILLE_DONNEES}} de fichiers (10-30 minutes sur HDD).
C'est précisément pour éviter que le conteneur fasse ce chown à chaque démarrage
qu'on a activé OWNCLOUD_SKIP_CHOWN=true.
On le fait une seule fois ici, manuellement.
Vérifier que .ocdata existe : ce fichier marqueur doit être présent
dans le répertoire des données pour qu'ownCloud le reconnaisse comme un datadirectory valide.
|
1 |
ls -la /home/docker/owncloud/data/files/.ocdata |
S'il n'existe pas :
|
1 2 |
touch /home/docker/owncloud/data/files/.ocdata chown 33:33 /home/docker/owncloud/data/files/.ocdata |
11.1 Pré-configurer les valeurs critiques de migration
Cette étape est obligatoire AVANT le premier démarrage.
L'entrypoint du conteneur génère un nouveau instanceid,
passwordsalt et secret. Mais la base de données importée
contient des mots de passe hachés avec l'ancien passwordsalt
et des données chiffrées avec l'ancien secret.
Si les valeurs ne correspondent pas, aucun utilisateur ne pourra se connecter.
On crée un fichier de configuration supplémentaire que ownCloud charge
après le config.php généré par l'entrypoint,
écrasant les valeurs générées par les bonnes :
|
1 2 3 4 5 6 7 8 9 10 |
cat > /home/docker/owncloud/data/config/migration.config.php << 'EOF' <?php $CONFIG = array( 'instanceid' => '{{OC_INSTANCEID}}', 'passwordsalt' => '{{OC_PASSWORDSALT}}', 'secret' => '{{OC_SECRET}}', ); EOF chown 33:33 /home/docker/owncloud/data/config/migration.config.php |
Vérification importante : après création, vérifiez que le tag PHP
est correct avec hexdump -C /home/docker/owncloud/data/config/migration.config.php | head -1.
La ligne doit commencer par 3c 3f 70 68 70 ().
Si vous voyez 26 6c 74 3b 3f 70 68 70, c'est que le fichier contient
l'entité HTML < au lieu du caractère < —
il faut le recréer en copiant depuis la page rendue du navigateur, pas depuis le code source HTML.
Pourquoi un fichier séparé et pas éditer config.php directement ?
Parce que l'entrypoint du conteneur régénère config.php
à chaque démarrage via gomplate (templates + variables d'environnement).
Les fichiers *.config.php dans le même répertoire sont chargés par ordre
alphabétique après config.php et écrasent ses valeurs.
migration.config.php (m > c) sera donc toujours chargé en dernier.
12) Premier démarrage ownCloud
|
1 2 |
cd /home/docker/owncloud/ docker compose up -d |
Surveiller les logs :
|
1 |
docker compose logs -f owncloud |
Le premier démarrage prend quelques minutes. L'entrypoint du conteneur :
- Génère
config.phpà partir des variables d'environnement - Détecte la base existante (importée en étape 10)
- Configure Redis et les paramètres système
Attendre que les conteneurs soient healthy :
|
1 |
docker compose ps |
Les 4 conteneurs doivent être Up : owncloud-server, owncloud-db, owncloud-redis, owncloud-cron.
12.1 Corriger les permissions du répertoire de sessions PHP
Le conteneur crée le répertoire /mnt/data/sessions au premier démarrage,
mais il peut être créé avec root:root comme propriétaire.
PHP (qui tourne en tant que www-data) ne peut alors pas y écrire de fichiers de session,
ce qui provoque une erreur 500 sur toutes les pages
(SessionNotAvailableException).
|
1 2 3 4 5 6 7 8 9 |
# Vérifier le propriétaire docker compose exec owncloud ls -la /mnt/data/sessions # Corriger si le propriétaire est root docker compose exec owncloud chown www-data:www-data /mnt/data/sessions docker compose exec owncloud chmod 700 /mnt/data/sessions # Redémarrer le conteneur pour appliquer docker compose restart owncloud |
Vérification rapide : tester avec curl -s http://127.0.0.1:8490/status.php.
Si la réponse est vide (Content-Length: 0) avec un code 500, c'est très probablement
ce problème de permissions sur le répertoire de sessions.
Après correction, la réponse doit être un JSON :
{"installed":true,"maintenance":false,...}.
12.2 Désactiver l'app files_antivirus
Si l'ancien serveur utilisait l'app files_antivirus (ClamAV),
elle est toujours activée dans la base de données importée.
En Docker, le socket ClamAV n'est pas disponible, ce qui génère des erreurs
en continu dans les logs :
Failed to connect to "/var/run/clamav/clamd.ctl".
|
1 |
docker compose exec owncloud occ app:disable files_antivirus |
Autres apps à vérifier : toute app qui dépend d'un service
système local (antivirus, LDAP, etc.) devra être désactivée ou reconfigurée
pour fonctionner dans l'environnement Docker.
13) Compléter la configuration
Les valeurs critiques (instanceid, passwordsalt, secret)
sont déjà en place grâce au fichier migration.config.php créé à l'étape 11.1.
Il reste à configurer le SMTP, la rétention et vérifier le tout.
13.1 Restaurer la configuration SMTP
|
1 2 3 4 5 6 7 8 9 10 |
docker compose exec owncloud occ config:system:set mail_domain --value="{{SMTP_DOMAIN}}" docker compose exec owncloud occ config:system:set mail_from_address --value="apps" docker compose exec owncloud occ config:system:set mail_smtpmode --value="smtp" docker compose exec owncloud occ config:system:set mail_smtpauthtype --value="LOGIN" docker compose exec owncloud occ config:system:set mail_smtpauth --value="1" --type=integer docker compose exec owncloud occ config:system:set mail_smtphost --value="{{SMTP_HOST}}" docker compose exec owncloud occ config:system:set mail_smtpname --value="apps@{{SMTP_DOMAIN}}" docker compose exec owncloud occ config:system:set mail_smtppassword --value="{{SMTP_PASSWORD}}" docker compose exec owncloud occ config:system:set mail_smtpsecure --value="ssl" docker compose exec owncloud occ config:system:set mail_smtpport --value="465" |
Mot de passe SMTP : la valeur ci-dessus provient directement de l'ancien config.php.
13.2 Restaurer les paramètres de rétention
|
1 2 3 |
docker compose exec owncloud occ config:system:set versions_retention_obligation --value="10" docker compose exec owncloud occ config:system:set trashbin_retention_obligation --value="auto, 180" docker compose exec owncloud occ config:system:set remember_login_cookie_lifetime --value="1296000" --type=integer |
13.3 Ajouter le nouveau serveur aux domaines de confiance
|
1 |
docker compose exec owncloud occ config:system:set trusted_domains 1 --value="{{IP_NOUVEAU}}" |
Permet l'accès via l'IP directe pendant les tests (avant la bascule DNS).
Le domaine {{DOMAINE_OWNCLOUD}} est déjà configuré via la variable
OWNCLOUD_TRUSTED_DOMAINS.
13.4 Activer le cron en arrière-plan
|
1 2 |
# Passer les tâches de fond en mode cron (au lieu d'AJAX) docker compose exec owncloud occ background:cron |
Cron intégré : l'image owncloud/server:10.16 inclut un cron interne
qui exécute occ system:cron toutes les 15 minutes (tâches de fond, nettoyage de sessions, etc.).
Les crons custom spécifiques (versions:cleanup, files:scan) sont gérés par le conteneur
owncloud-cron séparé (voir section 5.2).
Vérifier que le conteneur cron est actif :
|
1 2 |
docker compose ps cron docker compose logs cron |
Le conteneur owncloud-cron doit être Up et les logs doivent montrer
crond: crond (busybox 1.x.x) started, log level 8.
13.5 Vérifier la configuration complète
|
1 2 3 4 5 6 7 8 9 10 |
# Valeurs critiques de migration docker compose exec owncloud occ config:system:get instanceid docker compose exec owncloud occ config:system:get passwordsalt # Redis (file locking) docker compose exec owncloud occ config:system:get memcache.locking docker compose exec owncloud occ config:system:get redis host # Apps marketplace docker compose exec owncloud occ app:list |
Vérifier que :
instanceidretourne{{OC_INSTANCEID}}memcache.lockingretourne\OC\Memcache\Redisredis hostretourneredis- Les apps
files_texteditor,galleryettwofactor_totpapparaissent dans la liste
14) Lancer l'upgrade et le scan de fichiers
14.1 Activer le mode maintenance
|
1 |
docker compose exec owncloud occ maintenance:mode --on |
14.2 Exécuter l'upgrade
|
1 |
docker compose exec owncloud occ upgrade |
Si la version de la base correspond déjà à la version de l'image (10.16.0),
cette commande répondra ownCloud is already latest version. C'est normal.
14.3 Réparer la base de données
|
1 |
docker compose exec owncloud occ maintenance:repair |
Cette commande répare les partages cassés, les entrées orphelines dans le cache de fichiers,
les index manquants et les incohérences d'apps. Indispensable après une migration.
14.4 Corriger les chemins utilisateur dans la base
La base de données importée contient les chemins absolus de l'ancien serveur
dans la table oc_accounts. Chaque utilisateur a un champ home
qui pointe encore vers /home/{{USER_VESTA_OC}}/web/.../public_html/data/
au lieu de /mnt/data/files/ (le datadirectory Docker).
Sans cette correction, files:scan échouera avec
« Home storage for user X not writable » pour tous les utilisateurs migrés.
|
1 2 3 4 5 6 7 8 9 10 11 |
# Vérifier les chemins actuels docker compose exec db psql -U {{DB_USER}} -d {{DB_USER}} \ -c "SELECT user_id, home FROM oc_accounts;" # Corriger : remplacer l'ancien chemin par le nouveau docker compose exec db psql -U {{DB_USER}} -d {{DB_USER}} \ -c "UPDATE oc_accounts SET home = '/mnt/data/files/' || user_id WHERE home LIKE '/home/{{USER_VESTA_OC}}/%';" # Vérifier le résultat docker compose exec db psql -U {{DB_USER}} -d {{DB_USER}} \ -c "SELECT user_id, home FROM oc_accounts;" |
Pourquoi cette étape est nécessaire :
ownCloud stocke le chemin absolu du répertoire personnel de chaque utilisateur
dans la colonne home de oc_accounts. Lors d'une migration,
le datadirectory dans config.php est bien mis à jour
(via la variable d'environnement Docker), mais la base de données conserve
les anciens chemins. Le storage résout alors un chemin qui n'existe pas dans
le conteneur, ce qui déclenche une ForbiddenException dans le scanner.
Seul l'utilisateur admin (créé après le premier démarrage Docker)
aura déjà le bon chemin.
14.5 Scanner tous les fichiers
|
1 |
docker compose exec owncloud occ files:scan --all |
Scan long : avec {{TAILLE_DONNEES}} de données, le scan peut prendre
15 à 60 minutes selon le nombre de fichiers et la vitesse du HDD.
Il enregistre dans la base tous les fichiers copiés manuellement par rsync.
Ne pas interrompre cette commande.
14.6 Nettoyer les entrées orphelines
|
1 2 |
# Supprimer les entrées du cache de fichiers qui n'ont plus de fichier correspondant docker compose exec owncloud occ files:cleanup |
Pourquoi files:cleanup ?
Le rsync en 2 passes peut laisser quelques incohérences : des fichiers supprimés
entre le premier et le second rsync restent référencés dans la base.
Cette commande nettoie ces entrées orphelines.
14.7 Désactiver le mode maintenance
|
1 |
docker compose exec owncloud occ maintenance:mode --off |
15) Tester localement
|
1 |
curl -I http://127.0.0.1:8490 |
Réponse attendue :
|
1 2 |
HTTP/1.1 302 Found Location: https://{{DOMAINE_OWNCLOUD}}/login |
Vérifier aussi l'état des conteneurs :
|
1 |
docker compose ps |
Les 4 conteneurs doivent être Up (healthy) : owncloud-server, owncloud-db, owncloud-redis, owncloud-cron.
Vérifier le statut ownCloud :
|
1 |
docker compose exec owncloud occ status |
Doit afficher :
|
1 2 3 4 |
- installed: true - version: 10.16.0.0 - versionstring: 10.16.0 - edition: Community |
16) Basculer le DNS
Modifier l'enregistrement DNS A de {{DOMAINE_OWNCLOUD}}
pour pointer vers la nouvelle IP :
- Type : A
- Nom : owncloud
- Valeur :
{{IP_NOUVEAU}} - TTL : 300 (5 minutes, pour propager rapidement)
Vérifier la propagation :
|
1 |
dig {{DOMAINE_OWNCLOUD}} +short |
Doit retourner {{IP_NOUVEAU}}.
17) Activer Let's Encrypt
Après la propagation DNS (vérifier avec dig) :
- MyVestaCP → WEB →
{{DOMAINE_OWNCLOUD}}→ Edit - Cocher Enable SSL
- Cocher Let's Encrypt Support
- Save
Vérifier :
|
1 |
curl -I https://{{DOMAINE_OWNCLOUD}} |
18) Vérification post-migration
18.1 Se connecter à l'interface web
Ouvrir https://{{DOMAINE_OWNCLOUD}} dans un navigateur.
Se connecter avec un compte existant de l'ancien serveur.
Vérifier :
- Les fichiers sont présents et les aperçus fonctionnent
- Les partages entre utilisateurs sont intacts
- L'administration est accessible
18.2 Vérifier les clients de synchronisation
Les clients de synchronisation ownCloud détectent automatiquement le changement de serveur
via le DNS. Puisqu'on a préservé l'instanceid, ils ne demandent pas de reconfiguration.
Si un client demande de se reconnecter : cela signifie que l'instanceid
ne correspond pas. Vérifier avec docker compose exec owncloud occ config:system:get instanceid
qu'il retourne bien {{OC_INSTANCEID}}.
18.3 Arrêter l'ancien serveur ownCloud
Une fois la migration validée, désactiver ownCloud sur l'ancien serveur :
|
1 2 |
# Sur l'ancien serveur : laisser le mode maintenance actif # ou désactiver le vhost dans VestaCP |
Dépannage
Les valeurs instanceid / passwordsalt sont incorrectes
Vérifier que le fichier migration.config.php (créé en section 11.1)
est bien présent et contient les bonnes valeurs :
|
1 2 |
cat /home/docker/owncloud/data/config/migration.config.php docker compose exec owncloud occ config:system:get instanceid |
Si instanceid ne retourne pas {{OC_INSTANCEID}},
le fichier n'est pas chargé. Vérifier les permissions (chown 33:33)
et que le fichier est bien dans /home/docker/owncloud/data/config/.
Erreur SQLSTATE: permission denied for table oc_*
L'ancien dump contenait des ALTER OWNER pour un rôle différent.
Vérifier que le dump a été exporté avec --no-owner --no-acl.
Si le problème persiste, changer le propriétaire de toutes les tables :
|
1 2 |
docker compose exec db psql -U {{DB_USER}} -d {{DB_USER}} -c \ "DO \$\$ DECLARE r RECORD; BEGIN FOR r IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' LOOP EXECUTE 'ALTER TABLE public.' || r.tablename || ' OWNER TO {{DB_USER}};'; END LOOP; END \$\$;" |
Erreur 500 au premier accès
Vérifier les logs :
|
1 |
docker compose logs owncloud | tail -50 |
Causes fréquentes :
- Permission denied →
chown -R 33:33 /home/docker/owncloud/data/ - Database connection failed → vérifier que le conteneur
dbest running
et que les credentials correspondent .ocdatafile not found →touch /home/docker/owncloud/data/files/.ocdata && chown 33:33 /home/docker/owncloud/data/files/.ocdata
Le scan files:scan est extrêmement lent
Sur HDD avec beaucoup de petits fichiers, le scan peut être très lent.
Scanner utilisateur par utilisateur pour suivre la progression :
|
1 2 3 4 5 |
# Lister les utilisateurs docker compose exec owncloud occ user:list # Scanner un utilisateur spécifique docker compose exec owncloud occ files:scan admin |
Apps marketplace incompatibles
Si occ upgrade échoue à cause d'une app marketplace,
désactiver l'app problématique et relancer :
|
1 2 |
docker compose exec owncloud occ app:disable [nom_app] docker compose exec owncloud occ upgrade |
Erreur 500 avec SessionNotAvailableException
Symptômes : toutes les pages retournent un code 500, curl -v http://127.0.0.1:8490/status.php
montre un Content-Length: 0, et les headers contiennent un Set-Cookie qui crée
puis supprime immédiatement le cookie de session.
Diagnostiquer :
|
1 2 3 4 5 |
# Vérifier le gestionnaire de sessions et le répertoire docker compose exec owncloud php -i | grep session.save # Vérifier le propriétaire du répertoire docker compose exec owncloud ls -la /mnt/data/sessions |
Cause : le répertoire /mnt/data/sessions appartient à root:root.
PHP (www-data, UID 33) ne peut pas y écrire les fichiers de session.
|
1 2 3 |
docker compose exec owncloud chown www-data:www-data /mnt/data/sessions docker compose exec owncloud chmod 700 /mnt/data/sessions docker compose restart owncloud |
Nginx refuse de démarrer : Address already in use sur 8080/8443
Symptôme : systemctl restart nginx échoue avec
bind() to {{IP_NOUVEAU}}:8080 failed (98: Address already in use).
Diagnostiquer :
|
1 2 |
# Vérifier qui utilise ces ports ss -tlnp | grep -E '8080|8443' |
Cause : les ports 8080 et 8443 sont occupés par Apache
(le backend MyVestaCP). Le template Nginx généré par MyVestaCP utilise
%web_port% (8080) et %web_ssl_port% (8443) au lieu de 80/443.
Tous les autres domaines sur ce serveur utilisent correctement le port 80 pour Nginx.
|
1 2 3 4 5 |
# Corriger les ports sed -i 's/8080/80/' /home/{{USER_VESTA}}/conf/web/{{DOMAINE_OWNCLOUD}}.nginx.conf sed -i 's/8443 ssl/443 ssl/' /home/{{USER_VESTA}}/conf/web/{{DOMAINE_OWNCLOUD}}.nginx.ssl.conf nginx -t && systemctl restart nginx |
Logs inondés par files_antivirus : « Failed to connect to socket »
Symptôme : les logs ownCloud sont remplis de messages
Failed to connect to "/var/run/clamav/clamd.ctl" toutes les quelques secondes.
Cause : l'app files_antivirus était activée sur l'ancien serveur
(où ClamAV était installé). En Docker, le socket ClamAV n'existe pas.
|
1 |
docker compose exec owncloud occ app:disable files_antivirus |
Conteneur ownCloud bloque au démarrage (unhealthy)
Avec OWNCLOUD_SKIP_CHOWN=true, si des fichiers n'appartiennent pas à UID 33,
le conteneur peut démarrer mais être unhealthy.
Vérifier les permissions :
|
1 2 3 4 5 6 |
# Vérifier le propriétaire de quelques fichiers ls -la /home/docker/owncloud/data/config/ ls -la /home/docker/owncloud/data/files/ # Corriger si nécessaire chown -R 33:33 /home/docker/owncloud/data/ |
Erreur « Home storage for user X not writable » sur files:scan
Cette erreur apparaît quand les chemins utilisateur dans la table oc_accounts
pointent encore vers l'ancien serveur. Le message est trompeur — il ne s'agit pas
d'un problème de permissions fichiers mais d'un chemin qui n'existe pas dans le conteneur.
Diagnostiquer :
|
1 2 3 |
# Vérifier les chemins stockés dans la base docker compose exec db psql -U {{DB_USER}} -d {{DB_USER}} \ -c "SELECT user_id, home FROM oc_accounts;" |
Si la colonne home contient des chemins comme
/home/{{USER_VESTA_OC}}/web/.../data/username au lieu de
/mnt/data/files/username, appliquer la correction de la section 14.4 :
|
1 2 |
docker compose exec db psql -U {{DB_USER}} -d {{DB_USER}} \ -c "UPDATE oc_accounts SET home = '/mnt/data/files/' || user_id WHERE home LIKE '/home/{{USER_VESTA_OC}}/%';" |
Puis relancer le scan : docker compose exec owncloud occ files:scan --all
Récapitulatif de l'architecture finale
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Nouveau serveur ({{IP_NOUVEAU}}) ┌──────────────────────────────────────────────────────────────────────────────────────────── │ MyVestaCP Nginx (reverse proxy) │ │ ┌─────────────────────────────────────────────────────────────────────────────────────── │ │ │ :443 → owncloud-8490.stpl (odoo17, odoo8, pim, owncloud, etc.) │ │ │ └─────────────────────────────────────────────────────────────────────────────────────── │ │ │ │ Docker Compose : /home/docker/owncloud/ │ │ ┌─────────────────────────────────────────────────────────────────────────────────────────────────── │ │ │ owncloud-server owncloud-db (PG13) owncloud-redis owncloud-cron │ │ │ │ (:8490→:8080) postgres:13 redis:7-alpine docker:27-cli │ │ │ │ owncloud/server:10.16 vol: owncloud-db vol: owncloud-redis (crons) │ │ │ │ bind: ./data → /mnt/data │ │ │ └─────────────────────────────────────────────────────────────────────────────────────────────────── │ │ │ │ Aussi sur ce serveur : Odoo 17 (:8069-8369), Odoo 8 (:8469), Akeneo PIM (:8480) │ └──────────────────────────────────────────────────────────────────────────────────────────── |
Ports utilisés sur le serveur :
8069-8369— Odoo 17 (4 instances)8469— Odoo 88480— Akeneo PIM 78490— ownCloud 10
À suivre
ownCloud est maintenant opérationnel en Docker avec {{TAILLE_DONNEES}} de données,
reverse proxy et SSL. Dans les prochains articles :
- Serveurs de jeux (Counter-Strike 1.6, TeamSpeak 3, EmuLinker)
- Sauvegardes automatisées et stratégie de rétention
- Monitoring et alertes
0 commentaire