Mailcow est la solution de messagerie complète déployée sur l'infra LORVA. Elle gère l'ensemble des boîtes mail de la plateforme lorva.dev et taskori.app, ainsi que le relay SMTP utilisé par les applications internes (authentification, notifications, support).
| Attribut | Valeur |
|---|---|
| Hôte | lorva-public |
| Répertoire | /docker/mailcow/ |
| URL admin | https://mail.lorva.dev |
| Compose project | mailcowdockerized |
| Réseau Docker interne | mailcowdockerized_mailcow-network |
| Certificats | DNS-01 Cloudflare (via ACME intégré Mailcow) |
Mailcow est composé de 17 conteneurs Docker formant un stack autonome. Tous les conteneurs sont préfixés mailcowdockerized-*.
| Conteneur | Image | Rôle |
|---|---|---|
nginx-mailcow |
ghcr.io/mailcow/nginx:1.30.1-1 |
Reverse proxy interne, frontend HTTP/HTTPS |
postfix-mailcow |
ghcr.io/mailcow/postfix:3.7.11-2 |
MTA entrant/sortant (SMTP) |
postfix-tlspol-mailcow |
ghcr.io/mailcow/postfix-tlspol:1.8.23 |
Politique TLS dynamique pour Postfix |
dovecot-mailcow |
ghcr.io/mailcow/dovecot:2.3.21.1-2 |
MDA, IMAP/POP3, stockage des boîtes |
rspamd-mailcow |
ghcr.io/mailcow/rspamd:3.14.3-1 |
Filtrage spam, DKIM signing |
clamd-mailcow |
ghcr.io/mailcow/clamd:1.71 |
Antivirus ClamAV (healthy) |
olefy-mailcow |
ghcr.io/mailcow/olefy:1.15 |
Analyse OLE/Office (macros malveillantes) |
sogo-mailcow |
ghcr.io/mailcow/sogo:5.12.8-1 |
Webmail + CalDAV/CardDAV |
php-fpm-mailcow |
ghcr.io/mailcow/phpfpm:8.2.29-2 |
Backend PHP (interface admin) |
mysql-mailcow |
mariadb:10.11 |
Base de données Mailcow |
redis-mailcow |
redis:7.4.6-alpine |
Cache et files de messages |
memcached-mailcow |
memcached:alpine |
Cache SOGo |
unbound-mailcow |
ghcr.io/mailcow/unbound:1.25 |
Résolveur DNS interne (healthy) |
acme-mailcow |
ghcr.io/mailcow/acme:1.97 |
Gestion certificats Let's Encrypt (DNS-01) |
netfilter-mailcow |
ghcr.io/mailcow/netfilter:1.64 |
Fail2ban-like, ban IP automatique |
watchdog-mailcow |
ghcr.io/mailcow/watchdog:2.11 |
Supervision et auto-restart des services |
dockerapi-mailcow |
ghcr.io/mailcow/dockerapi:2.12 |
API Docker interne |
ofelia-mailcow |
mcuadros/ofelia:latest |
Scheduler de tâches (nettoyage, maintenance) |
Mailcow utilise son propre réseau isolé mailcowdockerized_mailcow-network (bridge). Le conteneur nginx-mailcow est également connecté au réseau traefik-public pour être exposé via Traefik.
| Domaine | Usage |
|---|---|
lorva.dev |
Domaine infra, communications internes |
taskori.app |
Domaine produit Taskori, notifications et support |
| Adresse | Usage | Intégration |
|---|---|---|
[email protected] |
Envois infra, alertes système | — |
[email protected] |
Emails transactionnels Taskori (invitations, 2FA, notifications) | taskori-auth, taskori-manage |
[email protected] |
Boîte support client | Chatwoot |
Les applications utilisent Mailcow comme relay SMTP. Les credentials ne sont pas stockés ici — consulter Flatnotes (note taskori-sync, taskori-auth-2fa, taskori-manage-backlog) ou les variables CI/CD GitLab.
| Paramètre | Valeur |
|---|---|
| Host | mail.lorva.dev |
| Port SMTP (STARTTLS) | 587 |
| Port SMTP (TLS/SSL) | 465 |
| Port SMTP (soumission) | 25 (restreint aux IPs internes) |
| Authentification | LOGIN ou PLAIN |
| TLS | Obligatoire (STARTTLS sur 587, TLS implicite sur 465) |
| Utilisateur | Adresse mail complète (ex: [email protected]) |
| Mot de passe | Voir Flatnotes |
const transporter = nodemailer.createTransport({
host: 'mail.lorva.dev',
port: 587,
secure: false, // STARTTLS
auth: {
user: '[email protected]',
pass: process.env.SMTP_PASSWORD, // variable d'environnement, jamais en clair
},
});
import smtplib
from email.mime.text import MIMEText
with smtplib.SMTP('mail.lorva.dev', 587) as smtp:
smtp.starttls()
smtp.login('[email protected]', os.environ['SMTP_PASSWORD'])
smtp.sendmail(from_addr, to_addr, msg.as_string())
Chatwoot est configuré pour recevoir les emails entrants sur [email protected] via IMAP et envoyer les réponses via SMTP. Cette intégration permet le ticketing support directement depuis les emails clients.
mail.lorva.dev, port 993 (TLS)mail.lorva.dev, port 587 (STARTTLS)stalwart-mail → section Chatwoot)/docker/chatwoot/In-Reply-To et ReferencesL'intégration Chatwoot ↔ Taskori (email-to-ticket) est documentée dans la note Flatnotes
stalwart-mail.
Le service d'authentification Taskori (taskori-auth) utilise [email protected] pour :
Configuration SMTP dans le .env de taskori-auth sur taskori-prod :
SMTP_HOST=mail.lorva.dev
SMTP_PORT=587
[email protected]
SMTP_PASS=<voir Flatnotes note taskori-auth-2fa>
SMTP_FROM="Taskori <[email protected]>"
Le service taskori-manage utilise également [email protected] pour les notifications de tâches, rappels, et envoi de factures automatiques (voir backlog note taskori-manage-backlog).
Plusieurs boîtes « de rôle » (support@, hello@, billing@, ops@, sales@, klibri@, [email protected]…) sont partagées vers un compte central de lecture, [email protected], afin de toutes les consulter depuis une seule session SOGo/IMAP sans jongler avec plusieurs logins.
Le partage repose sur les ACL IMAP de Dovecot + un abonnement du compte destinataire. Les dossiers partagés apparaissent côté destinataire dans le namespace Shared/<boîte-source>/<dossier> (ex. Shared/[email protected]/INBOX).
⚠️ Pas d'API Mailcow pour ça. Ni l'interface admin ni l'API REST Mailcow ne gèrent les ACL de partage de dossiers. Tout passe par
doveadmdans le conteneurmailcowdockerized-dovecot-mailcow-1.
[email protected]lookup read write write-seen write-deleted insert post expunge create delete admin (le lecteur peut lire, marquer lu, déplacer, supprimer).[email protected])ssh lorva-public
DC=mailcowdockerized-dovecot-mailcow-1
RIGHTS="lookup read write write-seen write-deleted insert post expunge create delete admin"
# 1) Accorder l'ACL sur les 3 dossiers de la boîte source
for f in INBOX Junk Sent; do
docker exec "$DC" doveadm acl set -u [email protected] "$f" \
[email protected] $RIGHTS
done
# 2) Abonner le compte destinataire aux dossiers partagés
for f in INBOX Junk Sent; do
docker exec "$DC" doveadm mailbox subscribe -u [email protected] \
"Shared/[email protected]/$f"
done
Le lecteur voit alors « [email protected] » dans la liste des dossiers partagés de SOGo (https://mail.lorva.dev). Rafraîchir / se reconnecter pour recharger l'arbre des dossiers.
DC=mailcowdockerized-dovecot-mailcow-1
# ACL posées sur la boîte source
for f in INBOX Junk Sent; do
echo "== $f =="; docker exec "$DC" doveadm acl get -u [email protected] "$f"
done
# Abonnements du compte lecteur
docker exec "$DC" doveadm mailbox list -s -u [email protected] | grep -i klibri
DC=mailcowdockerized-dovecot-mailcow-1
for f in INBOX Junk Sent; do
docker exec "$DC" doveadm acl delete -u [email protected] "$f" [email protected]
docker exec "$DC" doveadm mailbox unsubscribe -u [email protected] "Shared/[email protected]/$f"
done
Trash/Archive/Sent/Drafts/Junk/INBOX à la création d'une boîte → OK en général. Sinon : doveadm mailbox create -u <boîte> <dossier>.Shared/ (préfixe Dovecot/Mailcow par défaut). Sur un client IMAP externe (Apple Mail, Thunderbird), il faut parfois activer « afficher tous les dossiers » ; l'abonnement serveur est néanmoins déjà fait par doveadm mailbox subscribe.[email protected] par l'adresse cible.[email protected] : support@, hello@, ops@, sales@, billing@, klibri@ (lorva.dev) + [email protected].Le SPF autorise uniquement le serveur Mailcow à envoyer pour les domaines gérés. Entrées DNS Cloudflare à vérifier :
| Domaine | Enregistrement TXT | Valeur |
|---|---|---|
lorva.dev |
@ (SPF) |
v=spf1 mx a:mail.lorva.dev ~all |
taskori.app |
@ (SPF) |
v=spf1 mx a:mail.lorva.dev ~all |
Les clés DKIM sont générées et gérées par Rspamd dans Mailcow. Pour chaque domaine, Mailcow génère un enregistrement DNS à publier dans Cloudflare.
Vérifier la clé DKIM depuis l'interface admin :
Configuration → Domains → sélectionner le domaineDKIM → copier le TXT recordFormat de l'enregistrement :
| Domaine | Nom | Type | Valeur |
|---|---|---|---|
lorva.dev |
dkim._domainkey.lorva.dev |
TXT | v=DKIM1; k=rsa; p=<clé publique> |
taskori.app |
dkim._domainkey.taskori.app |
TXT | v=DKIM1; k=rsa; p=<clé publique> |
Vérification depuis la ligne de commande :
dig TXT dkim._domainkey.lorva.dev +short
dig TXT dkim._domainkey.taskori.app +short
| Domaine | Enregistrement | Valeur recommandée |
|---|---|---|
lorva.dev |
_dmarc.lorva.dev TXT |
v=DMARC1; p=quarantine; rua=mailto:[email protected]; adkim=s; aspf=s |
taskori.app |
_dmarc.taskori.app TXT |
v=DMARC1; p=quarantine; rua=mailto:[email protected]; adkim=s; aspf=s |
Vérification complète SPF/DKIM/DMARC :
# Tester SPF
dig TXT lorva.dev +short | grep spf
dig TXT taskori.app +short | grep spf
# Tester DMARC
dig TXT _dmarc.lorva.dev +short
dig TXT _dmarc.taskori.app +short
# Vérification complète via un outil externe
curl -s "https://mxtoolbox.com/SuperTool.aspx?action=mx%3alorva.dev"
Test d'envoi avec analyse :
Envoyer un mail de test à [email protected] depuis une boîte Mailcow, la réponse automatique indique le résultat SPF/DKIM/DMARC.
admintaskori-sync ou secrets infra)ssh lorva-public
cd /docker/mailcow
# Logs de tous les conteneurs mailcow
ssh lorva-public "docker compose -f /docker/mailcow/docker-compose.yml logs -f --tail=100"
# Logs d'un service spécifique
ssh lorva-public "docker compose -f /docker/mailcow/docker-compose.yml logs -f postfix-mailcow"
ssh lorva-public "docker compose -f /docker/mailcow/docker-compose.yml logs -f rspamd-mailcow"
ssh lorva-public "docker compose -f /docker/mailcow/docker-compose.yml logs -f dovecot-mailcow"
ssh lorva-public "docker compose -f /docker/mailcow/docker-compose.yml restart postfix-mailcow"
ssh lorva-public "docker compose -f /docker/mailcow/docker-compose.yml restart rspamd-mailcow"
ssh lorva-public "docker compose -f /docker/mailcow/docker-compose.yml down && docker compose -f /docker/mailcow/docker-compose.yml up -d"
Ne pas redémarrer manuellement le stack sans raison — le watchdog gère l'auto-restart des services défaillants.
ssh lorva-public "docker ps --filter name=mailcow --format 'table {{.Names}}\t{{.Status}}'"
La quarantaine est accessible depuis l'interface admin Mailcow :
Mail Setup → Quarantine# Se connecter au conteneur rspamd
ssh lorva-public "docker exec -it mailcowdockerized-rspamd-mailcow-1 bash"
# Voir les statistiques Rspamd
rspamc stat
# Apprendre un message comme spam
rspamc learn_spam < /path/to/mail.eml
# Apprendre un message comme ham (faux positif)
rspamc learn_ham < /path/to/mail.eml
Rspamd dispose d'une UI accessible en interne via l'interface admin Mailcow :
Configuration → Rspamd UI (protégée par le mot de passe admin Mailcow)
Mail Setup → Domains → bouton Add domainAprès création du domaine :
Mail Setup → Domains → cliquer sur le domaineDNS — Mailcow affiche tous les enregistrements à créer (MX, SPF, DKIM, DMARC)Les certificats TLS pour le nouveau domaine sont gérés automatiquement par le conteneur
acme-mailcowvia DNS-01 Cloudflare. Aucune action manuelle nécessaire.
Mail Setup → Mailboxes → Add mailboxMail Setup → Aliases → Add alias
Exemples utiles :
[email protected] → [email protected] (obligatoire RFC)[email protected] → [email protected] (obligatoire RFC)ssh lorva-public
cd /docker/mailcow
git fetch origin
git diff origin/master # vérifier les changements
git pull origin master
docker compose pull
docker compose up -d
Lire les release notes Mailcow avant toute mise à jour majeure : https://github.com/mailcow/mailcow-dockerized/releases
Le conteneur ofelia-mailcow (scheduler) gère automatiquement :
Les données critiques sont dans les volumes Docker gérés par Mailcow :
# Lister les volumes mailcow
ssh lorva-public "docker volume ls | grep mailcow"
# Backup du volume MySQL (données de config et comptes)
ssh lorva-public "docker exec mailcowdockerized-mysql-mailcow-1 mysqldump -u root -p<voir Flatnotes> mailcow > /backup/mailcow-mysql-$(date +%Y%m%d).sql"
# Backup des mails (Dovecot vmail)
ssh lorva-public "docker run --rm -v mailcowdockerized_vmail-vol-1:/data:ro -v /backup:/backup alpine tar czf /backup/vmail-$(date +%Y%m%d).tar.gz /data"
# Vérifier que les enregistrements DNS sont propagés
dig TXT lorva.dev +short
dig TXT dkim._domainkey.taskori.app +short
# Vérifier les logs Postfix pour l'envoi
ssh lorva-public "docker logs mailcowdockerized-postfix-mailcow-1 --since 1h | grep -i 'reject\|error\|dkim'"
# Vérifier les logs Rspamd
ssh lorva-public "docker logs mailcowdockerized-rspamd-mailcow-1 --since 1h | grep -i 'dkim\|fail'"
# Tester la connexion SMTP manuellement
openssl s_client -connect mail.lorva.dev:465
# Tester STARTTLS
openssl s_client -starttls smtp -connect mail.lorva.dev:587
# Vérifier que le compte SMTP existe et est actif
# → Interface admin : Mail Setup → Mailboxes → vérifier le statut
# Voir les IP bannies
ssh lorva-public "docker exec mailcowdockerized-netfilter-mailcow-1 cat /tmp/netfilter-bans.json"
# Débloquer une IP via l'interface admin
# Configuration → Fail2Ban → liste des bans → bouton unban
# Voir les événements watchdog
ssh lorva-public "docker logs mailcowdockerized-watchdog-mailcow-1 --since 24h | grep -i restart"
| Ressource | Lien |
|---|---|
| Interface admin | https://mail.lorva.dev |
| Documentation officielle Mailcow | https://docs.mailcow.email |
| Release notes Mailcow | https://github.com/mailcow/mailcow-dockerized/releases |
| Credentials SMTP | Flatnotes — notes taskori-auth-2fa, taskori-manage-backlog, taskori-sync |
| Intégration Chatwoot | Flatnotes — note stalwart-mail |
| Clés DKIM | Interface admin mail.lorva.dev → Mail Setup → Domains |
| Logs en cas d'incident | docker logs mailcowdockerized-postfix-mailcow-1 |