Nextcloud
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.
| Conteneur | Rôle |
|---|---|
| nextcloud-aio-mastercontainer | Interface d'administration AIO, orchestration des autres conteneurs |
| nextcloud-aio-apache | Point d'entrée HTTP, cible du reverse proxy (port 11000) |
| nextcloud-aio-nextcloud | Application (PHP-FPM) |
| nextcloud-aio-database | Base PostgreSQL |
| nextcloud-aio-redis | Cache et verrous |
| nextcloud-aio-notify-push | Notifications push vers les clients |
| nextcloud-aio-collabora | Édition bureautique en ligne |
| nextcloud-aio-talk | Serveur Talk HPB et TURN (port 3478) |
| nextcloud-aio-imaginary | Génération des aperçus |
| nextcloud-aio-whiteboard | Serveur temps réel du tableau blanc |
| nextcloud-aio-borgbackup | Sauvegarde Borg, démarré uniquement pendant une sauvegarde ou une restauration |
| nextcloud-aio-domaincheck | Vé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
| Variable | Valeur | Rôle |
|---|---|---|
| APACHE_PORT | 11000 | Port d'écoute d'Apache, en HTTP, pour le reverse proxy |
| APACHE_IP_BINDING | 127.0.0.1 | Apache 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_VALIDATION | false | La validation du domaine reste active (elle fonctionne grâce à la correction du hairpin NAT, voir plus bas) |
| NEXTCLOUD_DATADIR | <APPDATA>/Nextcloud-AIO/data | Dossier hôte des fichiers utilisateurs. Non modifiable après la première installation |
| NEXTCLOUD_UPLOAD_LIMIT | 16G | Taille maximale d'un envoi |
| NEXTCLOUD_MEMORY_LIMIT | 1024M | Limite mémoire PHP |
AIO ne lit ces variables que sur le mastercontainer : les autres conteneurs sont configurés depuis l'interface AIO.
Stack
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
- Déployer la stack dans Portainer.
- Ouvrir https://<IP_LAN>:<PORT_AIO> et accepter le certificat auto-signé.
- Noter la phrase de passe AIO affichée (elle protège l'interface AIO) et la ranger dans le gestionnaire de mots de passe.
- 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.
- 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.
- Régler le fuseau horaire et, pour Collabora, les dictionnaires (fr_FR).
- Si une version majeure est proposée, choisir une version supérieure ou égale à celle de l'ancienne instance (voir Pièges).
- 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 :
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 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 copié par le script de 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 :
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 :
3. Import
Copier l'archive dans le dossier de données AIO, puis l'importer dans le compte créé :
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
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ème | Cause | Solution |
|---|---|---|
| Import dans un compte vide ou dossier introuvable | Les identifiants sont sensibles à la casse : le dossier de données suit la casse exacte de l'identifiant | Cré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'espace | Appeler php /app/www/public/occ directement, avec --user abc et l'identifiant entre guillemets |
| Export en échec : permission refusée | Le dossier d'export créé par docker exec appartient à root | chown 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ées | Un fichier .zip.part est un export encore en cours ou interrompu | Ne 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 cible | Installer 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 Whiteboard | Remplacer EuroOffice par Collabora dans l'interface AIO |
| Tableau blanc qui ne se connecte pas | Le secret partagé du serveur Whiteboard n'est pas renseigné côté Nextcloud | Ré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 échec | Hairpin NAT : les conteneurs résolvent <DOMAINE_NEXTCLOUD> vers l'IP publique, que la box ne renvoie pas vers le LAN | Voir Hairpin NAT |
| Connexions lentes pour tout le monde, délais anti-bruteforce | Sans trusted_proxies, toutes les requêtes semblent venir de l'IP de SWAG : un échec de connexion ralentit tous les utilisateurs | Voir trusted_proxies |
| Conteneurs AIO mis à jour ou recréés hors d'AIO | Watchtower ne voit pas le label d'exclusion, présent seulement sur le mastercontainer | Voir Watchtower |
| Réglages SMTP ignorés | AIO ne lit pas de variables SMTP | Configurer 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 :
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 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 :
Ajouter tout nouveau conteneur nextcloud-aio-* à cette liste lors de l'activation d'un composant (ClamAV, recherche plein texte, enregistrement Talk…).