Skip to content

Déployer avec Docker Compose

This content is not available in your language yet.

Le fichier infra/compose/docker-compose.yml est le Compose officiel de Cenaclo : proxy-agnostique, il sert aussi bien derrière Coolify que lancé directement par un opérateur. Ce parcours s’adresse à celles et ceux qui veulent piloter Docker eux-mêmes, brancher leur propre reverse proxy ou intégrer le déploiement dans une chaîne d’intégration continue existante.

Si vous cherchez le chemin le plus court vers une instance en HTTPS, préférez le parcours Coolify.

  • Un hôte Docker Linux avec Docker Engine et le plugin Compose v2. Voir Dimensionner le serveur.
  • Un checkout du dépôt Cenaclo sur l’hôte de déploiement. Contrairement au template Coolify, qui tire des images publiées, ce Compose construit les services backend et web depuis les sources du dépôt. Le checkout est donc la référence de version de votre installation.
  • Un domaine public pointant vers l’hôte si vous voulez le TLS intégré (profil caddy).
Fenêtre de terminal
cp infra/compose/.env.example infra/compose/.env

Renseignez ensuite les valeurs réelles dans infra/compose/.env. Ce fichier contient des secrets : il n’est pas versionné et ne doit jamais l’être.

Les variables les plus structurantes :

Variable Rôle
APP_BASE_URL, CORS_ORIGINS URL publique de l’application, utilisée pour les liens et l’origine autorisée
POSTGRES_PASSWORD, DATABASE_URL Accès Postgres ; les deux doivent rester cohérents
JWT_PRIVATE_KEY, JWT_PUBLIC_KEY, AUTH_IP_SALT, EMAIL_HASH_PEPPER Secrets applicatifs, à générer une fois pour toutes
CENACLO_MASTER_KEY_SECRET Matériel de la master key du chiffrement au repos
INSTALL_KEYS_HOST_DIR Répertoire hôte des clés d’installation, /etc/cenaclo/keys par défaut
S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET Accès au stockage objet SeaweedFS embarqué
LIVEKIT_API_KEY, LIVEKIT_API_SECRET Identifiants LiveKit ; le secret doit faire au moins 32 caractères
LISTMONK_DB_PASSWORD, LISTMONK_ADMIN_PASSWORD Listmonk embarqué
VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT Clés Web Push ; le backend refuse de démarrer sans elles

Les secrets d’authentification et la master key se produisent en une fois :

Fenêtre de terminal
bash scripts/gen-keys.sh

Ce script produit exactement JWT_PRIVATE_KEY, JWT_PUBLIC_KEY, JWT_ACTIVE_KID, AUTH_IP_SALT et CENACLO_MASTER_KEY_SECRET. Il ne produit ni EMAIL_HASH_PEPPER ni les clés Web Push. Le pepper est accepté dès seize caractères : laissé à la valeur d’exemple du fichier, il passe la validation au démarrage, sans aucun message, et rend les empreintes d’email prévisibles. Générez-le donc explicitement :

Fenêtre de terminal
printf 'EMAIL_HASH_PEPPER=%s\n' "$(openssl rand -base64 32)"

Les clés Web Push ont leur propre commande de génération, indiquée en commentaire juste au-dessus des variables VAPID_* dans infra/compose/.env.example. Elle imprime les trois lignes au format .env ; reportez ensuite la clé publique dans VITE_VAPID_PUBLIC_KEY, l’argument de build de la SPA, sinon l’abonnement aux notifications échoue côté navigateur.

Sur le parcours Coolify, ces secrets sont générés au premier démarrage et vous n’avez rien à produire à la main.

Le service one-shot install-keys-init prépare INSTALL_KEYS_HOST_DIR sur l’hôte : il en fixe le propriétaire et le mode 0700. C’est dans ce répertoire que le wizard d’installation écrit la clé master.age. Le montage doit rester accessible en écriture au conteneur backend.

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml up -d

Services permanents : postgres (Postgres 18), redis (Redis 7), seaweedfs, livekit, livekit-egress, listmonk, backend et web. Quatre exécutions ponctuelles complètent l’amorçage puis sortent en code 0 : install-keys-init décrit ci-dessus, listmonk-db-init, listmonk-seed et livekit-egress-config. Une stack saine affiche donc huit conteneurs en cours d’exécution et quatre terminés dans docker compose ps --all.

Le backend applique ses migrations de schéma pendant son démarrage. Aucune commande de migration séparée n’est à lancer.

Par défaut, le Compose ne publie aucun port sur l’hôte. Les services web et backend restent joignables uniquement depuis le réseau Docker interne, nommé par INTERNAL_NETWORK_NAME (salon-internal par défaut). Cette neutralité est volontaire : elle permet de placer le proxy de votre choix devant la stack. Trois options.

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml --profile caddy up -d

Ce profil ajoute Caddy, qui publie ${CADDY_HTTP_PORT:-80} et ${CADDY_HTTPS_PORT:-443} sur l’hôte et obtient un certificat Let’s Encrypt pour CADDY_DOMAIN. Le domaine doit être public et pointer vers l’hôte : sans cela, la validation Let’s Encrypt échoue.

Le Caddyfile fourni sert un seul domaine pour toute l’application : il route les WebSockets (/ws/*, /socket.io/*) et les routes d’API vers backend:3000, avec ou sans préfixe /api, et tout le reste vers web:80. Aucune variable supplémentaire n’est nécessaire côté SPA dans cette configuration. Alignez simplement APP_BASE_URL et CORS_ORIGINS sur https://<CADDY_DOMAIN>.

Si vous utilisez déjà Nginx, Traefik ou un autre proxy, ne lancez pas le profil caddy. Deux branchements sont possibles :

  • Proxy conteneurisé : connectez-le au réseau ${INTERNAL_NETWORK_NAME:-salon-internal} et proxifiez vers web:80 et backend:3000.
  • Proxy installé sur l’hôte : ajoutez un fichier de surcharge Compose qui publie les ports voulus sur 127.0.0.1, puis pointez le proxy dessus.

Si votre proxy expose l’API sur un domaine distinct de la SPA, la SPA doit connaître cette URL. Le seul levier câblé par le fichier livré est l’argument de build VITE_API_BASE_URL : renseignez-le dans infra/compose/.env, puis reconstruisez l’image web. L’image sait aussi lire CENACLO_API_BASE_URL au démarrage du conteneur, mais le fichier livré ne déclare pas cette clé dans l’environment: du service web : posée seule dans infra/compose/.env, elle n’atteint jamais le conteneur et la SPA retombe silencieusement sur sa propre origine, sans message d’erreur. Pour l’utiliser sans reconstruire l’image, ajoutez un fichier de surcharge qui déclare la clé sur le service web. Dans les deux cas, la valeur doit commencer par http:// ou https://, ou valoir exactement /api pour un montage sous-chemin sur le même domaine.

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml up -d --build web

Dans tous les cas, les en-têtes HTTP de sécurité sont portés par le backend Fastify, pas par le proxy. Ils sont donc présents même sans Caddy et même sans Coolify ; un proxy qui les réécrirait dégraderait la configuration au lieu de l’améliorer.

Utile pour un environnement de préproduction interne. La stack reste alors accessible uniquement depuis le réseau Docker, ce qui suffit pour les vérifications par docker compose exec décrites plus bas.

Ouvrez https://<votre-domaine>/install et suivez les six étapes : domaine, compte administrateur, passphrase de chiffrement, double authentification TOTP, communauté, branding.

En mode Compose standalone, la clé d’installation vit hors du fichier .env, dans INSTALL_KEYS_HOST_DIR (/etc/cenaclo/keys), avec des permissions restreintes posées par install-keys-init.

Backoffice Cenaclo après installation : la checklist de démarrage indique 3 jalons sur 5 terminés — communauté créée, 2FA admin activée, premier salon créé — et propose de configurer le premier événement planifié et le premier lien d’invitation.

Capture prise à la fin d’un parcours Compose réel : une fois le wizard terminé, le backoffice s’ouvre sur la checklist de démarrage, qui liste les jalons restants avant d’inviter les premiers membres.

Profil coturn — live entièrement auto-hébergé

Section intitulée « Profil coturn — live entièrement auto-hébergé »
Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml --profile coturn up -d

Ce profil exige TURN_STATIC_AUTH_SECRET d’au moins 32 caractères ; le service refuse de démarrer sinon. Il utilise le réseau de l’hôte et une large plage UDP : il s’adresse aux opérateurs capables d’administrer le firewall. Les ports à ouvrir sont listés dans Firewall self-hosting. Le relay TURN mutualisé reste le chemin recommandé par défaut.

Variables associées : TURN_REALM, TURN_SERVER_NAME, TURN_EXTERNAL_IP.

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml --profile monitoring up -d

Ce profil ajoute GlitchTip et ses jobs d’amorçage sur le même Postgres, via les variables GLITCHTIP_*. Il est facultatif et n’est pas requis par l’application.

6. Rendre LiveKit joignable depuis les navigateurs

Section intitulée « 6. Rendre LiveKit joignable depuis les navigateurs »

Le serveur LiveKit embarqué est câblé pour l’usage interne : le conteneur livekit-egress le rejoint par ws://livekit:7880 et produit les replays sans aucun réglage. En revanche, le Compose ne publie pas les ports média LiveKit sur l’hôte, et LIVEKIT_PUBLIC_URL est vide par défaut, ce qui fait retomber le backend sur l’URL interne LIVEKIT_URL.

Conséquence à connaître avant d’annoncer le live à vos membres : tant que vous n’avez pas exposé LiveKit publiquement, un navigateur ne peut pas s’y connecter. Pour un live auto-hébergé en Compose, il faut donc :

  1. publier les ports de signalisation et de média LiveKit vers l’extérieur, via votre proxy pour le WebSocket et via une surcharge Compose pour les ports RTC ;
  2. renseigner LIVEKIT_PUBLIC_URL avec l’URL wss:// publique, que le backend remet aux clients ;
  3. ouvrir les ports correspondants sur le firewall.

Le parcours Coolify fait ce câblage pour vous en assignant un domaine au service livekit.

Si vous branchez un serveur LiveKit externe, alignez CENACLO_LIVE_EGRESS_ALLOWED_HOSTS sur l’hôte réellement joint par le conteneur Egress. Une liste qui ne contient pas cet hôte fait refuser la page de composition, donc aucun replay n’est produit.

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml config
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml ps
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec backend wget -q -O - http://127.0.0.1:3000/healthz
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec livekit-egress wget -q -O - http://127.0.0.1:7981/

La sonde du backend passe par docker compose exec parce que le Compose standalone ne publie pas le port 3000 sur l’hôte.

Les scripts d’exploitation embarqués dans l’image backend répondent tous à --help, ce qui permet de vérifier qu’ils sont présents et exécutables :

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec -T backend /usr/local/bin/cenaclo-backup.sh --help
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec -T backend /usr/local/bin/cenaclo-restore.sh --help
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec -T backend /usr/local/bin/cenaclo-purge.sh --help
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec -T backend /usr/local/bin/cenaclo-export-user.sh --help

Sauvegarde quotidienne, à planifier par cron sur l’hôte :

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec -T backend \
/usr/local/bin/cenaclo-backup.sh --output-dir /var/backups/cenaclo

Avant d’automatiser, renseignez une source de passphrase non interactive : CENACLO_BACKUP_PASSPHRASE_FILE pointant vers un fichier hors dépôt en mode 0600, ou à défaut CENACLO_BACKUP_PASSPHRASE dans infra/compose/.env. Détail des stratégies dans Sauvegardes self-hosting et Restaurer une instance.

Purge de rétention quotidienne :

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec -T backend \
/usr/local/bin/cenaclo-purge.sh --direct

Export RGPD d’un utilisateur :

Fenêtre de terminal
docker compose --env-file infra/compose/.env -f infra/compose/docker-compose.yml exec -T backend \
/usr/local/bin/cenaclo-export-user.sh --direct --user-email [email protected] --output /tmp/export.zip

Le wrapper infra/ops/cenaclo-update.sh enchaîne le pull des images, la reconstruction des services construits depuis les sources, le redémarrage puis l’attente de la sonde /healthz :

Fenêtre de terminal
bash infra/ops/cenaclo-update.sh \
--compose-file infra/compose/docker-compose.yml \
--env-file infra/compose/.env

Le wrapper ne fait aucun retour arrière automatique. Si la sonde de santé expire, il sort en erreur et laisse la stack en l’état. Pour revenir en arrière, replacez le checkout sur la version saine connue et relancez le wrapper. Le parcours détaillé, ainsi que le cas d’une migration destructive, sont décrits dans Mettre à jour.

En cas de problème, la page Dépannage recense les pannes les plus fréquentes.