CryptPad en Docker : deux domaines, un UID 4001, cinq étapes

Une instance CryptPad lancée avec un seul nom de domaine sert bien la page d’accueil, puis affiche un écran blanc au chargement des documents : config.example.js indique que la plupart des problèmes d’une nouvelle instance viennent du système d’isolation, qui exige deux origines distinctes, httpUnsafeOrigin et httpSafeOrigin. L’image officielle cryptpad/cryptpad:version-2026.5.1, publiée sur Docker Hub le 26 mai 2026, est construite sur node:lts-alpine et exécute le serveur avec l’utilisateur cryptpad, UID 4001 : les dossiers montés en volume doivent lui appartenir avant le premier démarrage.
Sommaire
- 1. Deux noms d’hôtes et un certificat qui couvre les deux
- 2. Créer les dossiers et les attribuer à l’UID 4001
- 3. Le fichier docker-compose.yml : version épinglée et ports en boucle locale
- 4. Démarrer, puis relever l’URL d’installation dans les journaux
- 5. Le contrôle /checkup/ compte les tests réussis
- Nginx ne doit pas ajouter ses propres en-têtes
- Ce qui disparaît au premier docker compose down
- Les tableurs et les présentations ne sont plus dans l’image
- FAQ
1. Deux noms d’hôtes et un certificat qui couvre les deux
CryptPad répartit le calcul sensible sur le domaine principal et l’interface dans une iframe servie par un domaine d’isolation. Les deux noms pointent vers la même machine et un seul certificat TLS doit couvrir les deux, sinon le test d’iframe du contrôle final échoue. CryptPad ne fonctionne pas dans un sous-dossier : l’accès se fait par un domaine ou un sous-domaine, pas par https://exemple.org/cryptpad/.
dig +short pad.exemple.org dig +short sable.pad.exemple.org
Ce que vous devez voir : deux lignes affichant la même adresse IPv4, celle du serveur qui hébergera le conteneur. Une seconde ligne vide bloque la suite, la demande de certificat ne pouvant pas inclure le domaine d’isolation. La marche à suivre pour un certificat unique couvrant deux noms est décrite dans la mise en HTTPS d’un service auto-hébergé.
2. Créer les dossiers et les attribuer à l’UID 4001
L’installation par Docker demande sept dossiers, créés puis attribués à l’identifiant utilisé par l’image, avant la première commande docker compose up. Sans cette étape, Docker crée lui-même les dossiers manquants en root et le script d’entrée, qui lance npm run build à chaque démarrage, ne peut plus écrire dedans.
mkdir -p data/blob data/block data/data data/files customize onlyoffice-dist onlyoffice-conf sudo chown -R 4001:4001 data customize onlyoffice-dist onlyoffice-conf
Le sel de connexion se règle à ce moment, jamais après la création des comptes : la documentation précise que le modifier plus tard casse la connexion de tous les utilisateurs existants. Générez une valeur, puis écrivez-la dans customize/application_config.js, un fichier placé dans un dossier monté et donc conservé d’un conteneur à l’autre.
openssl rand -hex 32 sudo chown -R 4001:4001 customize ls -ln data customize
Ce que vous devez voir : openssl renvoie une chaîne de 64 caractères hexadécimaux, et ls -ln affiche 4001 4001 dans les colonnes de propriétaire et de groupe pour les dossiers listés. Le fichier customize/application_config.js contient alors une ligne du type AppConfig.loginSalt = 'votre-chaine';.
3. Le fichier docker-compose.yml : version épinglée et ports en boucle locale
Le fichier livré dans le dépôt monte ./data/blob, ./data/block, ./customize, ./data/data, ./data/files, ./onlyoffice-dist et ./onlyoffice-conf, avec ulimits.nofile fixé à 1000000. Deux écarts rendent la mise en service plus prévisible : épingler la version plutôt que latest, et ne publier les ports que sur la boucle locale, Nginx assurant le TLS sur la même machine. Cette architecture reprend celle des autres services auto-hébergés de la rubrique Logiciels : un conteneur, des dossiers montés, une terminaison TLS sur l’hôte.
services:
cryptpad:
image: cryptpad/cryptpad:version-2026.5.1
hostname: cryptpad
restart: unless-stopped
environment:
- CPAD_MAIN_DOMAIN=https://pad.exemple.org
- CPAD_SANDBOX_DOMAIN=https://sable.pad.exemple.org
- CPAD_CONF=/cryptpad/config/config.js
volumes:
- ./data/blob:/cryptpad/blob
- ./data/block:/cryptpad/block
- ./customize:/cryptpad/customize
- ./data/data:/cryptpad/data
- ./data/files:/cryptpad/datastore
- ./onlyoffice-dist:/cryptpad/www/common/onlyoffice/dist
- ./onlyoffice-conf:/cryptpad/onlyoffice-conf
ports:
- "127.0.0.1:3000:3000"
- "127.0.0.1:3003:3003"
ulimits:
nofile:
soft: 1000000
hard: 1000000
docker compose config
Ce que vous devez voir : le YAML résolu, avec les deux domaines et les deux associations de ports, sans avertissement de variable manquante. Le port 3003 est celui du websocket, que le fichier Nginx d’exemple redirige depuis le chemin /cryptpad_websocket. Oublier cette redirection fait échouer le test websocket du contrôle final, sans message d’erreur dans les journaux du conteneur.
4. Démarrer, puis relever l’URL d’installation dans les journaux
Le script d’entrée de l’image fabrique /cryptpad/config/config.js à partir de config.example.js uniquement si le fichier est absent, en remplaçant httpUnsafeOrigin et httpSafeOrigin par les valeurs de CPAD_MAIN_DOMAIN et CPAD_SANDBOX_DOMAIN. Il lance ensuite npm run build pour générer les pages statiques, puis le serveur : ce premier passage est nettement plus long que les suivants.
docker compose up -d docker compose logs -f cryptpad docker compose ps
Ce que vous devez voir d’abord : l’avertissement littéral du script d’entrée, normal lorsque le conteneur fabrique sa configuration lui-même.
Warning: No config file provided for cryptpad
Ce que vous devez voir ensuite : en fin de journal, l’URL d’installation contenant un jeton unique, que la documentation indique de relever par docker compose logs. Ouvrez cette adresse avant de fermer la session : elle sert à créer le compte administrateur, à nommer l’instance, à choisir les applications activées et à fermer les inscriptions. docker compose ps finit par afficher healthy, l’image embarquant un contrôle curl -f http://localhost:3000/ déclenché toutes les minutes.
5. Le contrôle /checkup/ compte les tests réussis
L’adresse https://pad.exemple.org/checkup/ exécute une série de tests depuis le navigateur : présence des deux origines, chargement de l’iframe d’isolation, connexion au websocket, écriture puis suppression d’un bloc chiffré, en-têtes CSP, réponse de /upload-blob, accès en HTTPS. Le compte rendu commence par la version réellement servie et se termine par un ratio.
curl -sI https://pad.exemple.org/ | grep -i strict-transport-security
curl -s -o /dev/null -w '%{http_code}\n' https://pad.exemple.org/api/broadcast
Ce que vous devez voir : la première commande renvoie un en-tête strict-transport-security contenant max-age= suivi d’un nombre, la seconde renvoie 200, et la page de contrôle affiche un ratio du type 32 / 32 tests passed. Le nombre total de tests dépend de la configuration de l’instance, les applications OnlyOffice activées en ajoutant. Les avertissements qui subsistent portent sur adminEmail, la boîte de support chiffrée, les liens vers les conditions d’utilisation, la politique de confidentialité et les balises de prévisualisation générées par npm run build.
Nginx ne doit pas ajouter ses propres en-têtes
CryptPad produit lui-même ses en-têtes de sécurité à partir de sa configuration. Un add_header X-Content-Type-Options nosniff; ajouté dans le bloc Nginx se cumule avec celui de l’application, et le contrôle affiche alors, mot pour mot :
/api/config was served with duplicated or incorrect headers. Compare your reverse-proxy configuration against the provided example.
La cause est une valeur dupliquée, reconnaissable à la virgule ajoutée par Nginx, sur X-Content-Type-Options, Access-Control-Allow-Origin, Permissions-Policy ou X-XSS-Protection. Le remède tient en une suppression : retirer ces directives et ne conserver que l’en-tête HSTS de l’exemple. Le même contrôle vérifie qu’un fichier témoin répond dans /blob/ et /block/, et que /upload-blob ne renvoie pas 404.
Ce qui disparaît au premier docker compose down
config.js n’est écrit qu’une fois, uniquement s’il est absent, et le fichier de composition livré ne monte pas config/config.js. Toute valeur ajoutée à la main dans ce fichier, comme adminKeys ou maxUploadSize, part avec le conteneur recréé, qui repart de config.example.js. Le montage à ajouter est une ligne.
- ./config/config.js:/cryptpad/config/config.js
Le chemin doit exister côté hôte avant le démarrage, sinon Docker y crée un dossier vide et le script d’entrée ne peut plus écrire sa configuration. La copie du fichier généré se récupère depuis le conteneur en fonctionnement.
docker compose cp cryptpad:/cryptpad/config/config.js ./config/config.js
Ce que vous devez voir : après relance avec ce montage, docker compose logs ne contient plus la ligne Warning: No config file provided for cryptpad.
Les tableurs et les présentations ne sont plus dans l’image
Les applications texte, tableur et présentation de CryptPad reposent sur OnlyOffice, qui n’est plus embarqué dans l’image. La variable CPAD_INSTALL_ONLYOFFICE=yes déclenche install-onlyoffice.sh --accept-license --trust-repository à chaque démarrage du conteneur, et le commentaire du fichier livré rappelle qu’il faut lire et accepter la licence OnlyOffice avant de décommenter la ligne. Sans cette variable, l’instance fonctionne, mais ces trois formats ne s’ouvrent pas. Pour un besoin limité au stockage et au partage de fichiers, une instance de stockage personnel couvre le cas avec moins de dépendances.
FAQ
Peut-on se passer du second domaine ? Pas pour une instance publique. Sans httpSafeOrigin, l’interface cesse d’être isolée du domaine qui manipule les clés, et la documentation prévient qu’exploiter CryptPad en production sans ce système met les informations des utilisateurs en risque. Sur une machine de test, laisser la valeur vide fait servir l’isolation sur le port 3001, soit httpPort plus un, et les tests signalent alors deux origines non HTTPS.
Faut-il utiliser latest ou version-2026.5.1 ? Les deux étiquettes désignent la même image, empreinte sha256:1f89dcdd28c1375f675e47b1d0cf07a51f3e9a07a79dc93c8cddb3295b98a34d, publiée le 26 mai 2026. latest suivra la publication suivante : un docker compose pull fera donc monter la version sans prévenir. Les notes de version demandent de lire les notes intermédiaires avant de changer de version, sous peine de problème de configuration.
Quelle machine pour une petite instance ? La documentation cite Debian 12 comme distribution de référence, avec 2 Go de mémoire, 2 processeurs et 20 Go de stockage en minimum. L’image publiée existe en amd64 et en arm64, et l’architecture est identique pour les deux.
Que faut-il sauvegarder ? Les dossiers data/, data/files/, block/ et blob/, auxquels s’ajoutent config/config.js et customize/. Le contenu est chiffré par les clients avant d’atteindre le disque, ce qui autorise une copie hors site sans exposer les documents ; une sauvegarde par rsync suffit pour ces arborescences.
Pourquoi des documents disparaissent-ils après trois mois ? La conservation par défaut est de 90 jours pour un document qu’aucun compte enregistré n’épingle, 15 jours pour les données archivées avant suppression définitive et 365 jours pour les données d’un compte inactif. Dans config/config.js, inactiveTime: false désactive l’expiration des documents.