Files

7.7 KiB
Raw Permalink Blame History

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)        │
  └───────────────────────────┘

Login

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ère config.docker.inc.php depuis les ROUNDCUBEMAIL_*, inclut /var/roundcube/config/*.php, initialise/migre la base. Pas de réimplémentation.
  • Volume www partagé (rw côté Roundcube, ro côté nginx) : pattern documenté upstream pour la variante FPM. À chaque démarrage, l'entrypoint resynchronise /usr/src/roundcubemail vers le volume (bin/installto.sh) : une image reconstruite (version ou thème) est donc propagée au simple up -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 sur ALLOWED_PATHS/SUPPORTED_TYPES de static.php, cache 30 j, les URL portent ?s=<mtime>). Seuls index.php et static.php atteignent PHP ; installer.php et le reste renvoient 404.
  • Thème compilé au build : Elastic est livré en CSS minifié ; _variables.less/_styles.less ne sont pris en compte qu'à la compilation (@import (optional) dans styles/variables.less et styles/styles.less). Un étage node:22-alpine recompile styles, print et embed avec lessc (même commande que skins/elastic/Makefile). Aucune dépendance Node dans l'image finale.
  • Dictionnaires aspell et mailcap intégrés au build plutôt qu'installés à chaque démarrage (ROUNDCUBEMAIL_ASPELL_DICTS fait un apk add au runtime). mailcap fournit /etc/mime.types, absent de l'image upstream (sinon warning « Mimetype to file extension mapping doesn't work properly »).
  • des_key imposée par .env : sinon l'entrypoint la génère aléatoirement dans config.inc.php, perdue si le volume www est recréé.
  • Healthchecks : MariaDB healthcheck.sh --connect --innodb_initialized ; Roundcube : port FPM 9000 ; nginx : GET /fpm-ping relayé à php-fpm (chaîne nginx → FPM validée, URL limitée à 127.0.0.1). Ordre de démarrage piloté par depends_on: condition: service_healthy.
  • Réseau backend en internal: 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 *.php dans config/ (inclus par ordre alphabétique), puis docker compose restart roundcube. Référence : config/defaults.inc.php dans le conteneur. config/10-base.inc.php fixe 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 dans RC_PLUGINS.
  • Taille des pièces jointes : UPLOAD_MAX_FILESIZE applique 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