# 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](docs/apercu-login.png) ## Démarrage ```sh 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](https://github.com/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/`. 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=`). 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 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 ```sh 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 : ```sh 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