Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sa4nUG3GtbdEaPS6fA8eq1
7.7 KiB
Roundcube Webmail en docker-compose
Stack Roundcube 1.7.4 prête à l'emploi : nginx + php-fpm + MariaDB, skin Elastic recompilé avec la charte vBlog.io (surcharges LESS dans theme/).
:8080 (127.0.0.1 par défaut, derrière un reverse proxy TLS)
│
┌─────────────▼─────────────┐ réseau frontend (sortie IMAP/SMTP)
│ nginx 1.30-alpine │ sert le statique depuis le volume www (ro)
└─────────────┬─────────────┘
│ FastCGI :9000
┌─────────────▼─────────────┐
│ roundcube (php-fpm 8.4) │ image locale = roundcube/roundcubemail:1.7.4-fpm-alpine
│ │ + skin Elastic recompilé + aspell + mailcap
└─────────────┬─────────────┘
│ réseau backend (internal: pas d'accès sortant)
┌─────────────▼─────────────┐
│ mariadb 11.8 (LTS) │
└───────────────────────────┘
Démarrage
cp .env.example .env # renseigner mots de passe, RC_DES_KEY (24 car.), IMAP/SMTP
docker compose up -d --build
docker compose ps # les 3 services doivent passer "healthy"
Puis http://127.0.0.1:8080/. Le premier démarrage copie les sources dans le volume www et crée le schéma SQL (bin/initdb.sh), compter ~30 s.
Arborescence
| Chemin | Rôle |
|---|---|
compose.yaml |
Les 3 services, volumes, réseaux, healthchecks |
.env.example |
Toutes les variables (versions, BDD, IMAP/SMTP, plugins...) |
roundcube/Dockerfile |
Build multi-étage : compilation LESS (node) puis image finale |
roundcube/zz-healthcheck.conf |
ping.path php-fpm pour le healthcheck |
nginx/templates/default.conf.template |
vhost, rendu par l'entrypoint nginx (envsubst NGINX_*) |
config/*.php |
Config Roundcube additionnelle, montée sur /var/roundcube/config |
theme/ |
Surcharges du skin Elastic (_variables.less, _styles.less, fonts/, images/) |
scripts/sync-theme.sh |
Réimporte une customisation (défaut ../roundcube-custom/vblog) |
Choix techniques
- Image officielle
fpm-alpine(roundcube/roundcubemail-docker) : son entrypoint génèreconfig.docker.inc.phpdepuis lesROUNDCUBEMAIL_*, inclut/var/roundcube/config/*.php, initialise/migre la base. Pas de réimplémentation. - Volume
wwwpartagé (rw côté Roundcube, ro côté nginx) : pattern documenté upstream pour la variante FPM. À chaque démarrage, l'entrypoint resynchronise/usr/src/roundcubemailvers le volume (bin/installto.sh) : une image reconstruite (version ou thème) est donc propagée au simpleup -d. - nginx : depuis 1.7, seul
public_html/est le docroot et les ressources sont appelées en/static.php/<chemin>. Le vhost les sert directement depuis le volume (préfixes et extensions alignés surALLOWED_PATHS/SUPPORTED_TYPESdestatic.php, cache 30 j, les URL portent?s=<mtime>). Seulsindex.phpetstatic.phpatteignent PHP ;installer.phpet le reste renvoient 404. - Thème compilé au build : Elastic est livré en CSS minifié ;
_variables.less/_styles.lessne sont pris en compte qu'à la compilation (@import (optional)dansstyles/variables.lessetstyles/styles.less). Un étagenode:22-alpinerecompilestyles,printetembedaveclessc(même commande queskins/elastic/Makefile). Aucune dépendance Node dans l'image finale. - Dictionnaires aspell et
mailcapintégrés au build plutôt qu'installés à chaque démarrage (ROUNDCUBEMAIL_ASPELL_DICTSfait unapk addau runtime).mailcapfournit/etc/mime.types, absent de l'image upstream (sinon warning « Mimetype to file extension mapping doesn't work properly »). des_keyimposée par.env: sinon l'entrypoint la génère aléatoirement dansconfig.inc.php, perdue si le volumewwwest recréé.- Healthchecks : MariaDB
healthcheck.sh --connect --innodb_initialized; Roundcube : port FPM 9000 ; nginx :GET /fpm-pingrelayé à php-fpm (chaîne nginx → FPM validée, URL limitée à 127.0.0.1). Ordre de démarrage piloté pardepends_on: condition: service_healthy. - Réseau
backendeninternal: true: MariaDB n'a ni port publié ni accès sortant.
Thème vBlog.io
Les fichiers de theme/ et config/20-vblog.inc.php sont une copie de roundcube-custom/vblog/. Pour resynchroniser après modification :
sh scripts/sync-theme.sh [dossier_source]
docker compose up -d --build
Pour revenir à l'Elastic d'origine : vider theme/, supprimer config/20-vblog.inc.php, rebuild.
Configuration
- Variables : voir
.env.example(commentaires sur leur propre ligne : Compose peut intégrer un commentaire de fin de ligne à la valeur). - Options Roundcube : déposer des
*.phpdansconfig/(inclus par ordre alphabétique), puisdocker compose restart roundcube. Référence :config/defaults.inc.phpdans le conteneur.config/10-base.inc.phpfixe langue,proxy_whitelist, durée de session, rate-limit de login. - Plugins : intégrés →
RC_PLUGINS; tiers →RC_COMPOSER_PLUGINS(installés par composer au démarrage, nécessite l'accès à packagist) + ajout dansRC_PLUGINS. - Taille des pièces jointes :
UPLOAD_MAX_FILESIZEapplique la même limite à PHP et nginx. Le message encodé en base64 pèse ~1,37× la pièce jointe.
Reverse proxy TLS
La stack écoute en HTTP sur 127.0.0.1:8080. Le proxy frontal (Traefik, HAProxy, nginx...) doit transmettre X-Forwarded-For et X-Forwarded-Proto: https. Restreindre proxy_whitelist (config/10-base.inc.php) à l'IP du proxy en production. Si Roundcube est publié sous un sous-chemin, régler ROUNDCUBEMAIL_REQUEST_PATH.
Exploitation
docker compose logs -f roundcube # logs Roundcube et php-fpm (log_driver = stdout)
# Sauvegarde : base + volume enigma si le plugin est utilisé
docker compose exec -T mariadb sh -c 'mariadb-dump -uroot -p"$MARIADB_ROOT_PASSWORD" --single-transaction "$MARIADB_DATABASE"' > roundcube-$(date +%F).sql
Mise à jour Roundcube : modifier RC_VERSION dans .env puis docker compose up -d --build. L'entrypoint exécute installto.sh puis initdb.sh --update (migrations SQL). Sauvegarder la base avant.
Proxy TLS d'entreprise pendant le build (npm, apk) : fournir sa CA en secret BuildKit, optionnel :
docker build -f roundcube/Dockerfile --secret id=ca,src=/chemin/ca.pem -t local/roundcube:1.7.4 .
docker compose up -d --no-build
Validation effectuée
Stack construite et démarrée avec Docker 29.6 / Compose 5.3 : 3 services healthy, schéma SQL créé, login IMAP réussi contre un Dovecot 2.4.5 de test (utilisateur enregistré en base), charte vBlog appliquée (captures docs/), installer.php et static.php/config/... en 404, statique servi par nginx avec cache. Non testé dans l'environnement de validation (dépôt Alpine bloqué) : l'étape apk add des dictionnaires/mailcap, et l'envoi SMTP.
Sources
- Image et entrypoint : https://github.com/roundcube/roundcubemail-docker et https://hub.docker.com/r/roundcube/roundcubemail
- Options de config : https://github.com/roundcube/roundcubemail/blob/master/config/defaults.inc.php
- Skin Elastic, points d'extension LESS : https://github.com/roundcube/roundcubemail/tree/master/skins/elastic
- Image nginx (templates envsubst) : https://hub.docker.com//nginx ; image MariaDB (
healthcheck.sh) : https://hub.docker.com//mariadb
