0 Votes

Nextcloud

Version 2.1 par john le 2026/09/26 14:00

Cloud personnel, utilisé pour les notes et la veille technologique. Déployé avec Nextcloud All-in-One (AIO) derrière SWAG.

Pourquoi AIO

AIO remplace l'image LinuxServer.io et l'image communautaire nextcloud :

  • image maintenue par Nextcloud GmbH, testée avec chaque version de Nextcloud ;
  • composants intégrés et configurés automatiquement : sauvegarde Borg, Collabora, serveur Talk haute performance (HPB) avec TURN, Imaginary (aperçus), Whiteboard, notify_push ;
  • base PostgreSQL et Redis fournies, sans stack à maintenir à côté ;
  • mises à jour de Nextcloud, des conteneurs et des applications pilotées depuis une seule interface, précédées d'une sauvegarde.

L'image nextcloud ne fournit que l'application : base, cache, bureautique, Talk et sauvegarde restent à assembler et à maintenir à la main.

Architecture

Un seul conteneur est déclaré dans Portainer : le mastercontainer. Il crée et pilote les autres via le socket Docker.

ConteneurRôle
nextcloud-aio-mastercontainerInterface d'administration AIO, orchestration des autres conteneurs
nextcloud-aio-apachePoint d'entrée HTTP, cible du reverse proxy (port 11000)
nextcloud-aio-nextcloudApplication (PHP-FPM)
nextcloud-aio-databaseBase PostgreSQL
nextcloud-aio-redisCache et verrous
nextcloud-aio-notify-pushNotifications push vers les clients
nextcloud-aio-collaboraÉdition bureautique en ligne
nextcloud-aio-talkServeur Talk HPB et TURN (port 3478)
nextcloud-aio-imaginaryGénération des aperçus
nextcloud-aio-whiteboardServeur temps réel du tableau blanc
nextcloud-aio-borgbackupSauvegarde Borg, démarré uniquement pendant une sauvegarde ou une restauration
nextcloud-aio-domaincheckVérification du domaine, démarré uniquement avant la première installation

Les conteneurs créés par AIO sont reliés au réseau interne nextcloud-aio. nextcloud-aio-apache est en plus relié au réseau du reverse proxy.

Variables d'environnement

VariableValeurRôle
APACHE_PORT11000Port d'écoute d'Apache, en HTTP, pour le reverse proxy
APACHE_IP_BINDING127.0.0.1Apache n'est publié que sur la boucle locale de l'hôte : aucun accès direct depuis le réseau
APACHE_ADDITIONAL_NETWORK<RESEAU_SWAG>Réseau Docker du reverse proxy, auquel AIO relie nextcloud-aio-apache pour que SWAG l'atteigne par son nom
SKIP_DOMAIN_VALIDATIONfalseLa validation du domaine reste active (elle fonctionne grâce à la correction du hairpin NAT, voir plus bas)
NEXTCLOUD_DATADIR<APPDATA>/Nextcloud-AIO/dataDossier hôte des fichiers utilisateurs. Non modifiable après la première installation
NEXTCLOUD_UPLOAD_LIMIT16GTaille maximale d'un envoi
NEXTCLOUD_MEMORY_LIMIT1024MLimite mémoire PHP

AIO ne lit ces variables que sur le mastercontainer : les autres conteneurs sont configurés depuis l'interface AIO.

Stack

services:
 nextcloud-aio-mastercontainer:
   image: ghcr.io/nextcloud-releases/all-in-one:latest
   container_name: nextcloud-aio-mastercontainer   # nom imposé par AIO
   init: true
   restart: always
   ports:
      - "<IP_LAN>:<PORT_AIO>:8080"                  # interface AIO, LAN uniquement
   volumes:
      - nextcloud_aio_mastercontainer:/mnt/docker-aio-config
      - /var/run/docker.sock:/var/run/docker.sock:ro
   environment:
     APACHE_PORT: 11000
     APACHE_IP_BINDING: 127.0.0.1
     APACHE_ADDITIONAL_NETWORK: <RESEAU_SWAG>
     SKIP_DOMAIN_VALIDATION: "false"
     NEXTCLOUD_DATADIR: <APPDATA>/Nextcloud-AIO/data
     NEXTCLOUD_UPLOAD_LIMIT: 16G
     NEXTCLOUD_MEMORY_LIMIT: 1024M
   labels:
      - com.centurylinklabs.watchtower.enable=false

volumes:
 nextcloud_aio_mastercontainer:
   name: nextcloud_aio_mastercontainer              # nom imposé par AIO
  • Le nom du conteneur (nextcloud-aio-mastercontainer) et celui du volume (nextcloud_aio_mastercontainer) sont imposés : AIO les recherche sous ces noms exacts. Sans la clé name:, Compose préfixerait le volume du nom de la stack et AIO refuserait de démarrer.
  • Le port interne 8080 sert l'interface AIO en HTTPS avec un certificat auto-signé. Le port hôte <PORT_AIO> est libre, lié à l'IP LAN pour ne jamais être exposé à Internet.
  • NEXTCLOUD_DATADIR doit être fixé avant le premier démarrage : AIO l'enregistre à l'installation et ne le change plus ensuite. Le dossier est créé sur l'hôte avant de déployer la stack ; AIO lui applique lui-même le propriétaire www-data.

Premier démarrage

  1. Déployer la stack dans Portainer.
  2. Ouvrir https://<IP_LAN>:<PORT_AIO> et accepter le certificat auto-signé.
  3. Noter la phrase de passe AIO affichée (elle protège l'interface AIO) et la ranger dans le gestionnaire de mots de passe.
  4. Saisir le domaine <DOMAINE_NEXTCLOUD>. Le conteneur nextcloud-aio-domaincheck vérifie qu'il atteint bien cette instance à travers le reverse proxy : la conf SWAG doit donc être en place avant cette étape.
  5. Choisir les composants : Collabora, Talk, Imaginary, Whiteboard. Laisser ClamAV, la recherche plein texte, l'enregistrement Talk et le proxy du socket Docker désactivés s'ils ne servent pas.
  6. Régler le fuseau horaire et, pour Collabora, les dictionnaires (fr_FR).
  7. Si une version majeure est proposée, choisir une version supérieure ou égale à celle de l'ancienne instance (voir Pièges).
  8. Démarrer les conteneurs, puis noter le mot de passe du compte admin créé par AIO.

Reverse proxy (SWAG)

Fichier <APPDATA>/swag/config/nginx/proxy-confs/20-nextcloud.subdomain.conf :

server {
   listen 443 ssl;
   listen [::]:443 ssl;
   server_name <DOMAINE_NEXTCLOUD>;
   include /config/nginx/ssl.conf;
   client_max_body_size 0;

   location / {
       include /config/nginx/proxy.conf;
       include /config/nginx/resolver.conf;
       set $upstream_app nextcloud-aio-apache;
       set $upstream_port 11000;
       set $upstream_proto http;
       proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }
}
  • L'upstream est nextcloud-aio-apache:11000 en HTTP : SWAG termine le TLS, Apache d'AIO n'écoute qu'en clair derrière lui. Le nom se résout parce que APACHE_ADDITIONAL_NETWORK place ce conteneur sur le réseau de SWAG.
  • client_max_body_size 0 retire la limite de taille de nginx ; c'est NEXTCLOUD_UPLOAD_LIMIT qui s'applique.
  • Pas d'include Authelia : les clients de bureau et mobiles, WebDAV, CalDAV/CardDAV, Collabora et Talk ne savent pas passer une authentification placée devant Nextcloud. La protection repose sur la connexion Nextcloud, la 2FA et l'anti-bruteforce.

Commandes occ

AIO n'a pas de raccourci occ : on appelle PHP dans le conteneur applicatif, avec l'utilisateur www-data :

docker exec --user www-data nextcloud-aio-nextcloud php occ status                     # version, état
docker exec --user www-data nextcloud-aio-nextcloud php occ db:add-missing-indices     # après l'avertissement « index manquants »
docker exec --user www-data nextcloud-aio-nextcloud php occ maintenance:mode --on      # avant une opération lourde (--off ensuite)
docker exec --user www-data nextcloud-aio-nextcloud php occ files:scan --all           # fichiers ajoutés hors de Nextcloud
docker exec --user www-data nextcloud-aio-nextcloud php occ config:system:get trusted_proxies

Ne pas lancer occ upgrade ni app:update all à la main : AIO s'en charge lors de ses mises à jour.

Sécurité

  • Interface AIO accessible uniquement depuis le LAN (port lié à <IP_LAN>), protégée par la phrase de passe AIO.
  • Apache publié sur 127.0.0.1 uniquement : tout le trafic externe passe par SWAG.
  • Double authentification TOTP (application twofactor_totp). Elle n'est pas transférée par user_migration : chaque compte doit la réactiver dans Paramètres personnels > Sécurité après la migration.
  • trusted_proxies contient le sous-réseau Docker du reverse proxy, pour que l'anti-bruteforce voie la vraie IP des clients (voir Pièges).

Sauvegarde

  • Sauvegarde Borg intégrée à AIO, quotidienne à <HEURE>, vers <CHEMIN_BORG> sur l'hôte. Elle arrête les conteneurs, sauvegarde la base, le dossier de données et la configuration, puis relance tout ; si l'option est cochée dans l'interface AIO, elle applique ensuite les mises à jour disponibles.
  • Le mot de passe du dépôt Borg est affiché dans l'interface AIO après la première sauvegarde. Le conserver hors du serveur (gestionnaire de mots de passe) : sans lui, le dépôt est illisible et la restauration impossible si le serveur est perdu.
  • La restauration se fait depuis l'interface AIO d'une installation neuve : chemin du dépôt, mot de passe Borg, choix de l'archive.
  • Le dépôt <CHEMIN_BORG> est à inclure dans la Sauvegarde externe : un dépôt sur le même disque ne protège pas d'une panne du serveur.

Exploitation

  • TURN : rediriger le port 3478 en TCP et UDP de la box vers le serveur, sinon les appels Talk échouent hors du LAN. nextcloud-aio-talk le publie directement sur l'hôte, sans passer par SWAG.
  • Mises à jour : gérées par AIO (sauvegarde quotidienne suivie de la mise à jour, ou bouton dans l'interface AIO). Les conteneurs AIO sont exclus de Watchtower.
  • SMTP : à configurer dans Administration > Paramètres de base > Serveur de messagerie. AIO ne lit aucune variable d'environnement SMTP.
  • Avertissements de la vue d'ensemble à ignorer :
    • Talk HPB signalé en retard par rapport à Nextcloud 35 : l'image aio-talk suit avec un décalage, le service fonctionne ;
    • passerelle SIP non configurée : non utilisée ;
    • serveur d'enregistrement Talk non configuré : composant désactivé volontairement.

Migration depuis l'image LinuxServer.io

Faite avec l'application user_migration, compte par compte. Choisie parce que l'instance est petite (deux comptes) : cette méthode évite la conversion de la base MariaDB vers PostgreSQL et n'exige pas la même version de Nextcloud des deux côtés, contrairement à une migration complète (base + dossier de données).

L'archive contient notamment les fichiers, agendas et contacts du compte. Ne sont pas repris : l'appartenance aux groupes (dont admin), la 2FA, les partages entre comptes.

1. Export sur l'ancienne instance

Activer user_migration sur l'ancienne instance, puis :

# dossier d'export, créé en root par docker exec : le donner à l'utilisateur du serveur web (abc sur LinuxServer.io)
docker exec <ANCIEN_CONTENEUR> mkdir -p /data/export
docker exec <ANCIEN_CONTENEUR> chown abc:abc /data/export

# export d'un compte ; php occ appelé directement pour les identifiants contenant un espace
docker exec --user abc <ANCIEN_CONTENEUR> php /app/www/public/occ user:export "<IDENTIFIANT>" /data/export

Attendre la fin : l'archive finale est <IDENTIFIANT>.zip. Un fichier .zip.part est un export incomplet.

2. Création des comptes sur AIO

Créer chaque compte avec exactement le même identifiant, casse et espaces compris :

docker exec -it --user www-data nextcloud-aio-nextcloud php occ user:add --display-name="<NOM_AFFICHE>" "<IDENTIFIANT>"

3. Import

Copier l'archive dans le dossier de données AIO, puis l'importer dans le compte créé :

mkdir -p <APPDATA>/Nextcloud-AIO/data/import
cp <ARCHIVE>/<IDENTIFIANT>.zip <APPDATA>/Nextcloud-AIO/data/import/
chown -R 33:33 <APPDATA>/Nextcloud-AIO/data/import          # www-data dans le conteneur

docker exec --user www-data nextcloud-aio-nextcloud php occ user:import --user "<IDENTIFIANT>" "/mnt/ncdata/import/<IDENTIFIANT>.zip"

/mnt/ncdata est le point de montage de NEXTCLOUD_DATADIR dans nextcloud-aio-nextcloud. Supprimer le dossier import une fois les comptes vérifiés.

4. Après l'import

# le groupe admin n'est pas migré
docker exec --user www-data nextcloud-aio-nextcloud php occ group:adduser admin "<IDENTIFIANT>"
  • Les agendas sont recréés sous le nom migrated-<nom_d_origine> : les renommer si besoin et mettre à jour les abonnements CalDAV des appareils.
  • Réactiver la 2FA de chaque compte.
  • Reconnecter les clients (bureau, mobile, DAVx⁵) : l'URL ne change pas, mais leurs sessions appartenaient à l'ancienne instance.
  • Arrêter l'ancienne stack une fois tout vérifié, puis retirer son dump de la Sauvegarde.

Pièges rencontrés

ProblèmeCauseSolution
Import dans un compte vide ou dossier introuvableLes identifiants sont sensibles à la casse : le dossier de données suit la casse exacte de l'identifiantCréer le compte AIO avec la casse exacte de l'ancien identifiant
Export d'un identifiant contenant un espace : « user not found »Le raccourci occ de LinuxServer.io relaie mal les arguments et coupe l'identifiant à l'espaceAppeler php /app/www/public/occ directement, avec --user abc et l'identifiant entre guillemets
Export en échec : permission refuséeLe dossier d'export créé par docker exec appartient à rootchown vers l'utilisateur du serveur web (abc côté LinuxServer.io, www-data / uid 33 côté AIO)
Import en échec ou données tronquéesUn fichier .zip.part est un export encore en cours ou interrompuNe jamais importer un .zip.part ; relancer l'export jusqu'à obtenir le .zip
Import refuséArchive produite par une version de Nextcloud plus récente que la cibleInstaller une version majeure d'AIO supérieure ou égale à celle de l'ancienne instance
Page d'administration Whiteboard en erreur « Provided key is too short »EuroOffice embarque sa propre bibliothèque JWT, chargée à la place de celle de WhiteboardRemplacer EuroOffice par Collabora dans l'interface AIO
Tableau blanc qui ne se connecte pasLe secret partagé du serveur Whiteboard n'est pas renseigné côté NextcloudRécupérer le secret avec docker exec nextcloud-aio-whiteboard printenv JWT_SECRET_KEY et le saisir dans Administration > Tableau blanc
Collabora, Talk ou Whiteboard injoignables depuis Nextcloud ; validation du domaine en échecHairpin NAT : les conteneurs résolvent <DOMAINE_NEXTCLOUD> vers l'IP publique, que la box ne renvoie pas vers le LANVoir Hairpin NAT
Connexions lentes pour tout le monde, délais anti-bruteforceSans trusted_proxies, toutes les requêtes semblent venir de l'IP de SWAG : un échec de connexion ralentit tous les utilisateursVoir trusted_proxies
Conteneurs AIO mis à jour ou recréés hors d'AIOWatchtower ne voit pas le label d'exclusion, présent seulement sur le mastercontainerVoir Watchtower
Réglages SMTP ignorésAIO ne lit pas de variables SMTPConfigurer le serveur de messagerie dans Administration > Paramètres de base

Hairpin NAT

Le DNS local (voir AdGuard Home) réécrit <DOMAINE_NEXTCLOUD> vers <IP_LAN> du serveur. Docker est configuré pour l'utiliser comme résolveur de tous les conteneurs, dans /etc/docker/daemon.json :

{
 "hosts": ["unix:///var/run/docker.sock"],
 "log-driver": "json-file",
 "log-opts": { "max-size": "50m", "max-file": "3" },
 "dns": ["<IP_DNS_LOCAL>", "<DNS_SECOURS>"],
 "min-api-version": "1.24"
}

La clé dns s'applique après systemctl restart docker, qui redémarre tous les conteneurs. Vérification :

docker exec nextcloud-aio-nextcloud getent hosts <DOMAINE_NEXTCLOUD>     # doit renvoyer <IP_LAN>

Le DNS de secours n'est interrogé que si le DNS local ne répond pas ; dans ce cas le domaine repasse sur l'IP publique et le problème réapparaît jusqu'au retour du DNS local.

trusted_proxies

Ajouter le sous-réseau du réseau Docker de SWAG, à l'index suivant les entrées déjà posées par AIO :

docker network inspect <RESEAU_SWAG> --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'     # -> <SOUS_RESEAU_PROXY>
docker exec --user www-data nextcloud-aio-nextcloud php occ config:system:get trusted_proxies  # repérer le prochain index libre
docker exec --user www-data nextcloud-aio-nextcloud php occ config:system:set trusted_proxies <INDEX> --value="<SOUS_RESEAU_PROXY>"

Reprendre le masque exact du réseau : un masque plus large ferait confiance à l'en-tête X-Forwarded-For envoyé par d'autres machines.

Watchtower

AIO crée ses conteneurs sans le label com.centurylinklabs.watchtower.enable=false : il faut les lister nommément dans la variable WATCHTOWER_DISABLE_CONTAINERS de Watchtower :

WATCHTOWER_DISABLE_CONTAINERS=nextcloud-aio-apache,nextcloud-aio-borgbackup,nextcloud-aio-collabora,nextcloud-aio-database,nextcloud-aio-domaincheck,nextcloud-aio-eurooffice,nextcloud-aio-imaginary,nextcloud-aio-mastercontainer,nextcloud-aio-nextcloud,nextcloud-aio-notify-push,nextcloud-aio-redis,nextcloud-aio-talk,nextcloud-aio-whiteboard

Ajouter tout nouveau conteneur nextcloud-aio-* à cette liste lors de l'activation d'un composant (ClamAV, recherche plein texte, enregistrement Talk…).