Installation de Nextcloud derrière Traefik v3 via docker compose, avec OnlyOffice connecté automatiquement et des sauvegardes borg vers BorgWarehouse.
Brouillons de note de blog : https://codimd.communecter.org/s/QicjJT2X7
Les exemples ici sont avec les DNS suivants :
- nuage.lamiete.fr
- onlyoffice.lamiete.fr
À changer bien sûr avec vos propres adresses :)
| Service | Image | Version (.env) |
|---|---|---|
nextcloud_app |
nextcloud:${NEXTCLOUD_TAG} |
NEXTCLOUD_TAG=33 |
nextcloud_db |
postgres:${POSTGRES_TAG}-bookworm |
POSTGRES_TAG=18 |
nextcloud_redis |
redis:7.4 |
— |
onlyoffice |
onlyoffice/documentserver:${ONLYOFFICE_TAG} |
ONLYOFFICE_TAG=10.0 |
Le docker-compose.yml est le même sur toutes les instances (lamiete,
codecommun…) : ce qui change d'un serveur à l'autre vit dans le .env
(versions, domaine, ROUTER_NAME) et, si besoin, dans un
docker-compose.override.yml local (ignoré par git).
Historique des migrations, bugs rencontrés et corrections : changelog/
(procédures de restauration comprises).
make up / down / restart démarre, arrête, redémarre la stack
make logs suit les logs de Nextcloud
make ps état des conteneurs
make occ CMD="status" lance une commande occ
make init configure la sauvegarde : clé SSH, dépôt BWH, cron root (sudo)
make backup sauvegarde borg maintenant (sudo)
make check vérifie que la dernière sauvegarde est restaurable (sudo)
make list liste les archives du dépôt (sudo)
make mount / umount monte les archives en lecture seule (ARCHIVE=<nom>, sudo)
make verifier les versions du .env collent-elles à l'installé ? (rien ne change)
make update correctifs : sauvegarde, puis Nextcloud + OnlyOffice (sudo)
make upgrade-check la majeure Nextcloud suivante est-elle possible ?
make upgrade Nextcloud : majeure suivante, apps vérifiées (sudo)
make onlyoffice (re)connecte OnlyOffice depuis le JWT_SECRET du .env
make pg-upgrade PG_CIBLE=19 PostgreSQL : version majeure (sudo)
make up et make update passent d'abord par make verifier : ils refusent de
démarrer sur une combinaison de versions dangereuse (voir « Mises à jour »).
La logique vit dans scripts/ ; le Makefile n'est qu'une table des matières.
- Docker avec le plugin compose, l'utilisateur dans le groupe
docker;borg,flock,curletopensslsur l'hôte (sauvegarde) ;sudo. - Traefik v3 (dépôt TraefikV3) déjà en place, avec :
- le réseau externe
frontend(docker network create frontend) ; - un certresolver nommé
myresolver; - un middleware
crowdsec@filedans son provider file (sans lui, Traefik rejette le routeur : Nextcloud injoignable).
- le réseau externe
- DNS :
nuage.<DOMAIN>etonlyoffice.<DOMAIN>vers le serveur.
Deux fichiers, à ne jamais commiter (voir .gitignore), en chmod 600 :
.env(stack) — copierenv_example:POSTGRES_*,DOMAIN,JWT_SECRET,ROUTER_NAME, et les versionsNEXTCLOUD_TAG,POSTGRES_TAG,ONLYOFFICE_TAG. Générer le secret OnlyOffice :openssl rand -hex 32.ROUTER_NAMEsuffixe les routeurs Traefik : unique par instance si plusieurs Nextcloud partagent le même Traefik (ex.lamiete,codecommun).COMPOSE_PROJECT_NAME: unique par instance sur un même serveur, à fixer avant le premiermake up(ex.nextcloud_codecommun). Vide, c'est le nom du dossier (nextcloudpour lamiete). Deux instances avec le même nom se prendraient leurs conteneurs :make verifierrefuse ce cas.- Pour une instance existante, les versions sont celles déjà installées
(
make verifierle confirme) ; une base PostgreSQL ≤ 17 demande en plusPOSTGRES_MOUNT=/var/lib/postgresql/data.
scripts/.env(sauvegarde) — copierscripts/env_example:BORG_PREFIX,BORG_REPO,BORG_PASSPHRASE(remplis parmake init, voir « 6. Sauvegardes »),NC_MAINTENANCE,AGE_MAX_HEURES. À part : la passphrase n'a rien à faire avec les variables du compose.
Puis : make up (PostgreSQL démarre d'abord ; Nextcloud attend qu'il accepte
les connexions), et l'installateur web sur https://nuage.<DOMAIN> :
compte admin, base PostgreSQL, hôte nextcloud_db, base / utilisateur / mot
de passe = POSTGRES_DB / POSTGRES_USER / POSTGRES_PASSWORD du .env.
Aucun port n'est publié sur l'hôte : tout passe par Traefik (réseau frontend),
y compris les échanges Nextcloud ↔ OnlyOffice, qui utilisent les adresses
publiques en HTTPS. Pas de container_name non plus : les conteneurs
s'appellent <projet>-<service>-1, et les scripts passent par
docker compose. Sur le réseau frontend, partagé, les noms courts
(onlyoffice, nextcloud_app) sont ambigus dès qu'il y a deux instances :
ne jamais s'en servir comme adresse (make onlyoffice utilise les URL
publiques).
Réglages propres à un serveur : dans docker-compose.override.yml (ignoré par
git, chargé par docker compose comme par les scripts), pas dans le compose
commun. Exemple, si le serveur ne sait pas se joindre par sa propre IP publique
(NAT sans hairpin) :
services:
nextcloud_app:
extra_hosts: ["nuage.codecommun.coop:172.18.0.1", "onlyoffice.codecommun.coop:172.18.0.1"]
onlyoffice:
extra_hosts: ["nuage.codecommun.coop:172.18.0.1", "onlyoffice.codecommun.coop:172.18.0.1"](172.18.0.1 : passerelle du réseau frontend, voir docker network inspect frontend.)
'trusted_proxies' =>
array (
1 => '172.18.0.0/24', // sous-réseau de frontend : docker network inspect frontend
),
'overwritehost' => 'nuage.lamiete.fr',
'overwriteprotocol' => 'https',
'overwrite.cli.url' => 'https://nuage.lamiete.fr',
'trusted_domains' =>
array (
0 => 'nuage.lamiete.fr',
),
'memcache.locking' => '\OC\Memcache\Redis',
'redis' =>
array (
'host' => 'nextcloud_redis',
'port' => 6379,
'timeout' => 0.0,
'password' => '',
),make occ CMD="db:add-missing-indices"
make occ CMD="maintenance:repair --include-expensive"
make occ CMD="config:system:set maintenance_window_start --type=integer --value=1"
make occ CMD="config:system:set default_phone_region --value=FR"
make occ CMD="config:system:set serverid --type=integer --value=1"
make occ CMD="app:disable app_api" # sauf si vous utilisez des Ex-Apps# crontab de l'utilisateur (tâches de fond Nextcloud)
*/5 * * * * /usr/bin/docker compose -f /poule/miete/nextcloud/docker-compose.yml exec -T -u www-data nextcloud_app php -f /var/www/html/cron.php
# crontab root, posée par make init (heure demandée) : la sauvegarde lit data/,
# propriété de www-data
0 0 * * * bash /poule/miete/nextcloud/scripts/backup_nextcloud.sh >> /var/log/borg-nextcloud-<prefixe>.log 2>&1Instance déjà sauvegardée (lamiete) : make init refuse de toucher le dépôt,
la ligne root a été posée à la main ; la vérifier avec sudo crontab -l.
Plusieurs instances : une heure différente chacune (chaque sauvegarde met
Nextcloud en maintenance).
Toutes les 5 minutes, comme le recommande Nextcloud : avec @hourly, les tâches
de fond (notifications, nettoyages…) prenaient jusqu'à une heure de retard.
Nextcloud doit être en mode « Cron » (Administration → Paramètres de base), ou :
make occ CMD="background:cron".
Plus besoin d'aller chercher le token dans le conteneur ni de le coller dans
l'admin : une seule source de vérité, JWT_SECRET dans le .env.
- le conteneur
onlyofficele reçoit en variable d'environnement (JWT_ENABLED,JWT_SECRET,JWT_HEADER: Authorization) ; make onlyofficel'écrit (chiffré) dans le connecteur Nextcloud avec les URL (DocumentServerUrl,DocumentServerInternalUrl,StorageUrl), puis vérifie la connexion.
make onlyoffice
# [onlyoffice] OK — Nextcloud et le Document Server se parlent.make update le relance automatiquement. Rejouable à volonté. Sur une
instance neuve : après l'installateur web (il faut un Nextcloud installé).
Document vide dans l'éditeur ? Un bloqueur de pub (uBlock Origin…) bloque
Analytics.js, dont l'éditeur a besoin pour démarrer. Désactiver le bloqueur suronlyoffice.lamiete.fr, puis Ctrl+Maj+R. Ce fichier ne contacte pas Google : le traceur est désactivé dans le code d'OnlyOffice.
cp scripts/env_example scripts/.env && chmod 600 scripts/.env
make initmake init (le script du kit BorgWarehouse, comme pour Keycloak, Docuseal et
Etherpad, adapté à root) : clé SSH dédiée dans scripts/.ssh/, création du
dépôt sur BorgWarehouse (token API avec la seule permission « create », ou à
la main), passphrase, borg init, affichage de la passphrase et de la clé du
dépôt à mettre au coffre, ligne de cron root, première sauvegarde,
make check. Rejouable s'il s'interrompt. Il refuse de toucher un dépôt
qui contient déjà des archives (une nouvelle passphrase les rendrait
illisibles) : sur une instance déjà sauvegardée, make check suffit. Il
refuse aussi un dépôt qu'il ne peut pas lire (BorgWarehouse arrêté, sauvegarde
en cours, clé refusée, passphrase fausse) : corriger, puis relancer. En
méthode manuelle, relancer après avoir collé la clé publique dans BorgWarehouse.
Sans make init : créer le dépôt dans BorgWarehouse, renseigner
scripts/.env, make backup, make check, puis mettre au coffre la clé du
dépôt (borg key export) avec la passphrase. Sans scripts/.ssh/, la
sauvegarde utilise la clé SSH de root (cas de l'instance lamiete, configurée
ainsi avant make init).
Ce que fait make backup : mode maintenance → pg_dump -Fc → une archive
borg de toute la stack (sauf database/, remplacée par le dump) → sortie de
maintenance (garantie même en cas d'échec) → prune
(7 jours, 30 quotidiennes, 12 hebdomadaires, puis mensuelles).
Pour une instance clonée depuis nextcloud31 ou nextcloud32 (ou à la main).
Les dossiers data/ et database/ gardent les mêmes chemins : seuls le code
du dépôt et la configuration changent.
Principe : les deux dépôts n'ont pas d'historique commun, un git pull
est donc impossible. On fait pointer le dossier vers ce dépôt et on remplace
les fichiers suivis par git. .env, data/ et database/ ne sont pas suivis :
git n'y touche pas.
cd /chemin/de/l/instance
mkdir -p ~/avant-unification
# 0. Sauvegarde à la main (l'ancien dépôt n'a pas de make backup).
# Nom du service de la base dans l'ancien compose : docker compose config --services
docker compose exec -T nextcloud_db sh -c 'pg_dump -U "$POSTGRES_USER" -Fc "$POSTGRES_DB"' \
> ~/avant-unification/base.dump </dev/null
# + une copie de data/ si aucune sauvegarde des fichiers n'existe
# 1. Garder une trace de l'existant
git status --short > ~/avant-unification/status.txt
git diff HEAD > ~/avant-unification/modifs-locales.diff # retouches faites sur le serveur (indexées ou non)
cp -a docker-compose.yml compose-onlyoffice.yml README.md .env ~/avant-unification/ 2>/dev/null
docker compose ps; docker ps --filter name=onlyoffice # ce qui tourne vraiment
# 1 bis. L'ancien dépôt ne doit suivre AUCUNE donnée ni secret : le checkout
# de l'étape 3 SUPPRIME les fichiers suivis qui n'existent pas ici.
git ls-files | grep -E '^(\.env|data/|database/)' # doit ne rien afficher, sinon :
# git rm -r --cached <ces chemins> && git commit -m "ne plus suivre les donnees"
# (les retire de git, PAS du disque). Le commit est INDISPENSABLE : sans lui,
# le `git reset --hard` de l'étape 3 les remettrait dans leur version suivie.
# Vérifier ensuite : la commande ci-dessus n'affiche plus rien.
# 2. Arrêter l'ancienne stack AVEC l'ancien compose (il connaît ses container_name)
docker compose -f compose-onlyoffice.yml down # seulement si OnlyOffice tournait par lui
docker compose down
# 3. Basculer sur ce dépôt. Les fichiers suivis modifiés sur place (tracés à
# l'étape 1 dans modifs-locales.diff) font refuser le checkout : on les
# remet d'abord comme dans l'ancien dépôt.
git reset --hard # fichiers SUIVIS seulement : .env/data/database intacts
git remote set-url origin git@github.com:CoopCodeCommun/nextcloud.git
git fetch origin
git checkout -B main origin/main # remplace les fichiers suivis, garde .env/data/databaseSi git refuse encore (« would be overwritten »), un fichier non suivi porte
le nom d'un fichier du dépôt : le déplacer dans ~/avant-unification/, puis
relancer. Disparaissent du dossier : les fichiers de l'ancien dépôt absents de
celui-ci (compose-onlyoffice.yml, anciens scripts…) ; ils restent dans
~/avant-unification/ et dans l'historique de l'ancien dépôt.
4. Compléter le .env (voir env_example) avec les versions qui tournent
aujourd'hui, pas celles visées :
NEXTCLOUD_TAG=32 # grep OC_VersionString data/www/html/version.php
POSTGRES_TAG=17 # sudo cat database/PG_VERSION
POSTGRES_MOUNT=/var/lib/postgresql/data # base <= 17 : ancienne disposition
ONLYOFFICE_TAG=10.0
ROUTER_NAME=codecommun
COMPOSE_PROJECT_NAME=nextcloud_codecommun # si une autre instance tourne sur ce serveur
JWT_SECRET=... # vide ? openssl rand -hex 32Les variables inutiles de l'ancien .env (POSTGRES_HOST,
NEXTCLOUD_TRUSTED_DOMAINS…) peuvent rester : rien ne les lit.
Puis la sauvegarde, dans un dépôt borg propre à l'instance avec un
BORG_PREFIX à elle : cp scripts/env_example scripts/.env && chmod 600 scripts/.env. make init vient à l'étape 5, une fois la stack démarrée (il
fait la première sauvegarde).
5. Redémarrer et monter les versions, une étape à la fois :
make verifier # tout doit être OK, sinon corriger le .env
make up && make onlyoffice # même versions qu'avant, OnlyOffice reconnecté
make init # dépôt borg, cron, 1re sauvegarde + check
# (dépôt déjà configuré : make backup && make check)
make update # correctifs
make pg-upgrade PG_CIBLE=18 # 17 -> 18 (retire POSTGRES_MOUNT)
make upgrade-check && make upgrade # 32 -> 33PostgreSQL avant Nextcloud, comme pour lamiete : si l'ancienne image
n'était pas -bookworm (ex. postgres:17 sur Debian 13), les index texte
peuvent ne plus correspondre au tri de la glibc de l'image figée
(« collation version mismatch » dans les logs). Le dump / restauration de
make pg-upgrade les reconstruit ; la migration de schéma de Nextcloud se
fait ensuite sur une base saine. Nextcloud 32 fonctionne avec PostgreSQL 18
(simple avertissement).
Lancer make update, make upgrade et make pg-upgrade hors de l'heure
de la sauvegarde (cron root) : elle met Nextcloud en maintenance.
Adapter enfin les crontabs (section Installation) : sans container_name, la
ligne cron.php passe par docker compose -f …/docker-compose.yml exec.
Ne pas modifier les *_TAG du .env à la main (sauf OnlyOffice) : chaque
composant a sa commande, et les garde-fous refusent le reste.
| Ce qu'on fait | Commande | Garde-fou si on se trompe |
|---|---|---|
| correctifs (33.0.x, 18.x, 10.0.x) | make update |
— |
| Nextcloud majeure suivante | make upgrade-check puis make upgrade |
make up/update refusent une autre majeure que l'installée |
| PostgreSQL majeure | make pg-upgrade PG_CIBLE=19 |
refus si POSTGRES_TAG ≠ version des données (sinon : base vide créée à côté) |
| OnlyOffice 10.1, 11… | ONLYOFFICE_TAG à la main, puis make update OO_CONFIRME=1 |
refus sans OO_CONFIRME=1 |
Les vérifications se font avant la sauvegarde et avant tout arrêt : un refus ne coupe rien.
make update suit les correctifs de NEXTCLOUD_TAG (33 → 33.0.x) :
sauvegarde → apps à jour → arrêt propre d'OnlyOffice → redémarrage → attente de
la migration (jusqu'à 30 min, TIMEOUT=3600 make update pour plus) → index
et colonnes manquants → apps → reconnexion OnlyOffice (non bloquante : en cas
d'échec, make onlyoffice).
Si l'attente échoue avec « migration de la base NON faite »
(needsDbUpgrade) : le nouveau code est en place mais sa migration n'a pas
tourné. make occ CMD=upgrade, puis make update pour les finitions.
Majeure suivante :
make upgrade-check # vérifie, ne change rien
make upgrade # sauvegarde + montéemake upgrade vise installée + 1, jamais plus, et refuse :
- si l'image
nextcloud:<N+1>n'existe pas encore ; - si elle a moins de 2 correctifs (
34.0.0,34.0.1: on attend ;MIN_CORRECTIF=0pour forcer) ; - si une app activée n'a pas de version stable pour la cible sur l'app store :
elle serait désactivée par la montée (
APPS_OK=1pour passer outre).
Puis il écrit NEXTCLOUD_TAG=<N+1> dans le .env et enchaîne sur
make update. En cas d'échec avant la migration, le tag est remis.
Pour 33 → 35 : deux make upgrade successifs.
Ensuite, si make occ CMD=setupchecks le demande :
make occ CMD="maintenance:repair --include-expensive".
Vérifier aussi que la version de PostgreSQL est supportée par la cible (exigences système de Nextcloud).
Les correctifs du tag (10.0 → 10.0.x) arrivent avec make update.
Pour changer de version (10.1, 11…) : lire les notes de version, vérifier que
le connecteur (app onlyoffice de Nextcloud) la supporte, puis :
# dans le .env : ONLYOFFICE_TAG=10.1
make update OO_CONFIRME=1make pg-upgrade PG_CIBLE=19Dump / restauration, comptage ligne à ligne des tables, mots de passe re-hachés
en scram-sha-256, POSTGRES_TAG (et POSTGRES_MOUNT) mis à jour dans le
.env, retour arrière automatique en cas d'échec. Vérifier avant que la version
de Nextcloud supporte la cible. L'ancienne base est gardée en database.pgXX/
et les dumps en scripts/pg-upgrade/ (ignorés par git et par la sauvegarde) :
les supprimer une fois rassuré.
Activer le mode maintenance :
make occ CMD="maintenance:mode --on"
Scanner les fichiers sur disque dur et reconstruire la base de donnée. Utile en cas de suppression de fichier depuis le terminal, attention peut être très long :
docker compose exec --user www-data nextcloud_app php occ files:scan jonas
docker compose exec --user www-data nextcloud_app php occ files:scan --path="Ekopratik/files/Ekopratik/2. Administration/2.2 Salariat/2.2.1 En cours/"Arborescence en erreur (getOwner() on null dans l'interface Fichiers) :
réparer le cache, à relancer jusqu'à ce qu'il annonce 0 entrée :
make occ CMD="files:repair-tree"
Cleaner la corbeille :
make occ CMD="trashbin:cleanup Ekopratik"
La commande magique si jamais tu vois des soucis de chiffrement sur un fichier :
docker compose exec --user www-data nextcloud_app php occ encryption:fix-encrypted-version Guillaume --path=<file>
ex :
docker compose exec --user www-data nextcloud_app php occ encryption:fix-encrypted-version Guillaume --path=La\ Raffinerie\ \(1\)/Groupe\ Economique/Inter-location/SUIVI\ FACTURATION\ 2022\ -\ ARCHIVE.xlsx
Voir les logs d'erreur sur l'UI : https://nuage.lamiete.fr/settings/admin/logging
- https://belginux.com/nextcloud-corriger-les-avertissements-de-securite/#erreur-reverse-proxy
- Cycle de vie des versions Nextcloud : https://github.com/nextcloud/server/wiki/Maintenance-and-Release-Schedule
- Notes de version OnlyOffice : https://github.com/ONLYOFFICE/DocumentServer/blob/master/CHANGELOG.md