Héberger son email avec Mailcow sur un VPS
Hébergez un serveur mail avec mailcow sur un VPS : ouvrez le port 25, réglez les DNS MX/SPF/DKIM/DMARC/PTR et visez 10/10 sur mail-tester.
Ce que vous allez construire
Un serveur mail complet sur une machine qui vous appartient : SMTP pour envoyer et recevoir, IMAP pour garder votre téléphone et votre ordinateur portable synchronisés, un client webmail, et un filtre anti-spam qui note chaque message dans les deux sens. mailcow-dockerized regroupe Postfix, Dovecot, Rspamd, le webmail SOGo, MariaDB, Redis et un client ACME dans une seule stack Docker Compose : le logiciel n'est donc pas le plus difficile. Vous l'aurez en marche en une demi-heure.
Le plus difficile, c'est tout ce qui l'entoure. L'email est le seul service où le reste d'Internet se méfie activement d'un serveur tout neuf, et l'écart entre « ça marche » et « Gmail avale silencieusement chaque message » se résume à quatre enregistrements DNS et à un paramètre de réputation d'IP que vous ne contrôlez peut-être pas entièrement. Lisez les prérequis ci-dessous avant de louer quoi que ce soit. Si, après les avoir lus, vous décidez que l'effort sur la réputation n'en vaut pas la peine, c'est une réponse légitime : notre tour d'horizon de ce qui vaut vraiment la peine d'être auto-hébergé en 2026 classe l'email dans la catégorie « seulement si vous êtes sérieux » pour exactement ces raisons.
Les prérequis SONT le projet
Ratez-en un seul et vous enverrez du courrier qui n'arrivera jamais. Grosso modo dans l'ordre de la fréquence à laquelle chacun fait échouer les gens :
Le port 25 sortant doit être ouvert. Votre serveur livre le courrier à Gmail et à Microsoft via le port TCP 25. Une grande partie des fournisseurs VPS et cloud bloquent le port 25 sortant par défaut pour lutter contre le spam, et ce blocage est silencieux : rien ne renvoie d'erreur au démarrage, tout semble sain, et le courrier reste simplement dans la file d'attente pour toujours. Testez-le avant d'installer quoi que ce soit. S'il est bloqué, la seule solution est un ticket de support demandant à votre fournisseur de l'ouvrir ; certains le font pour les comptes anciens, d'autres ne le feront jamais.
Une IP propre avec une réputation utilisable. Les IP de VPS recyclées figurent souvent déjà sur des listes de blocage à cause du spam d'un locataire précédent. Vérifiez la vôtre sur un service comme la recherche Spamhaus ou mxtoolbox avant de vous engager. Une IP listée signifie des rejets que vous ne pourrez contourner par aucun code.
Le contrôle du DNS ainsi qu'un enregistrement PTR correct. Vous devez ajouter des enregistrements à la zone de votre domaine, et il vous faut un DNS inverse (PTR) pour l'IP du serveur qui pointe vers votre nom d'hôte mail. Le PTR n'est presque jamais défini dans votre panneau DNS : il réside chez le propriétaire de l'IP, il se règle donc dans le panneau de contrôle de votre fournisseur VPS ou par ticket.
6 Gio de RAM et 2 vCPU constituent le plancher confortable. Le minimum propre à mailcow est de 6 Gio de RAM plus 1 Gio de swap pour une installation privée, avec 8 Gio conseillés dès qu'une poignée d'utilisateurs en dépendent. En dessous d'environ 2,5 Gio, generate_config.sh propose de désactiver l'antivirus ClamAV pour que le noyau ne se mette pas à tuer des conteneurs. Prévoyez 20 Go de SSD pour commencer.
Un nom DNS, pas une IP nue. Choisissez un nom d'hôte comme mail.example.com. Ce nom unique devient votre MAILCOW_HOSTNAME, le sujet de votre certificat TLS, votre cible PTR et votre bannière SMTP. Gardez-le cohérent partout.
Étape 1 : Prouver que le port 25 sortant est ouvert
Faites ceci en premier. Tout le reste est peine perdue si cela échoue. Depuis le VPS tout neuf, essayez d'ouvrir une conversation SMTP avec un vrai serveur mail :
sudo apt update && sudo apt install -y netcat-openbsd
nc -vz -w 5 gmail-smtp-in.l.google.com 25Un résultat qui fonctionne est immédiat :
Connection to gmail-smtp-in.l.google.com (142.250.x.x) 25 port [tcp/smtp] succeeded!Un port bloqué reste suspendu pendant les cinq secondes complètes, puis échoue :
nc: connect to gmail-smtp-in.l.google.com port 25 (tcp) timed out: Operation now in progressCe délai d'attente dépassé, c'est le blocage. Il s'agit d'un filtre réseau côté fournisseur, pas de votre pare-feu, aucun changement local ne le corrige donc. Ouvrez un ticket : « Veuillez activer le port TCP 25 sortant pour mon VPS à l'adresse <IP> ; j'exploite un serveur mail légitime. » N'installez pas mailcow tant que ceci ne renvoie pas « succeeded ». Notez que le port 25 entrant (les autres serveurs qui vous joignent) est un chemin distinct et il est généralement ouvert : c'est le côté sortant que les fournisseurs brident.
Étape 2 : Publier les enregistrements DNS maintenant
Les changements DNS mettent du temps à se propager, publiez donc tout ce que vous pouvez avant d'installer. Supposons que votre domaine soit example.com, votre hôte mail mail.example.com, et l'IP 10.0.0.10. Dans votre zone, créez :
mail.example.com. A 10.0.0.10
mail.example.com. AAAA 2001:db8::10 ; only if you have IPv6
example.com. MX 10 mail.example.com.
example.com. TXT "v=spf1 mx -all"
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:postmaster@example.com"L'enregistrement SPF dit « seul mon MX peut envoyer pour ce domaine, rejette le reste ». Démarrez DMARC à p=none afin de pouvoir surveiller les rapports sans faire rebondir votre propre courrier ; resserrez vers p=quarantine puis p=reject une fois l'alignement prouvé. Deux enregistrements manquent encore volontairement : DKIM, que mailcow génère pour vous à l'étape 6, et PTR, que vous réglez maintenant dans le panneau de votre fournisseur.
Réglez le PTR (DNS inverse) de 10.0.0.10 vers mail.example.com, la valeur exacte de MAILCOW_HOSTNAME. C'est l'enregistrement que la plupart des gens oublient le plus, et les grands fournisseurs rejettent à cause de lui. Si votre panneau n'a pas de champ rDNS, ouvrez un ticket.
Étape 3 : Installer Docker
mailcow a besoin de Docker Engine avec le plugin Compose v2. Utilisez le script pratique officiel de Docker plutôt que le paquet docker.io d'Ubuntu, qui ne fournit aucun plugin Compose :
curl -fsSL https://get.docker.com | sudo sh
sudo docker compose versionVous devriez voir une ligne Docker Compose version v2.x. Si docker compose version affiche docker: 'compose' is not a docker command, Docker Engine est installé mais pas le plugin Compose. Installez le plugin depuis le dépôt de Docker : relancez le script ci-dessus, ou suivez notre guide des bases de Docker Compose, qui met en place les deux depuis le dépôt apt de Docker lui-même.
Étape 4 : Cloner mailcow et générer la configuration
cd /opt
sudo git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
umask
sudo ./generate_config.shVérifiez d'abord que umask affiche 0022 : mailcow refuse de se construire avec un masque de fichier impair, et un shell root Ubuntu 24.04 tout neuf vous donne déjà 0022. Le script demande ensuite la seule chose qui compte : le nom d'hôte pleinement qualifié. Saisissez mail.example.com : la valeur doit correspondre exactement à votre enregistrement A et à votre PTR. Il écrit mailcow.conf, l'unique fichier d'environnement que toute la stack lit. Ouvrez-le si vous devez changer les ports web (HTTP_PORT, HTTPS_PORT) ou désactiver ClamAV sur une petite machine :
MAILCOW_HOSTNAME=mail.example.com
HTTP_PORT=80
HTTPS_PORT=443
SKIP_CLAMD=n # set to y to drop the virus scanner on a <2.5 GiB boxSKIP_FTS=y est l'autre levier sur une machine à faible RAM : la recherche en texte intégral est le deuxième gouffre à mémoire que citent les docs de mailcow, et la désactiver ne vous coûte que la recherche dans le corps des messages dans le webmail.
Laissez HTTP_PORT=80 et HTTPS_PORT=443 sauf si autre chose sur l'hôte les occupe déjà : le client ACME intégré de mailcow a besoin que le port 80 soit joignable depuis Internet pour obtenir le certificat. C'est pourquoi vous n'exécutez pas une installation séparée nginx plus Certbot sur la même machine ; mailcow émet et renouvelle son propre TLS en interne, et un second service qui squatte les ports 80/443 casse cela.
Étape 5 : Démarrer la stack et se connecter
sudo docker compose pull
sudo docker compose up -d
sudo docker compose psLe pull récupère environ deux douzaines d'images ; accordez-lui quelques minutes. Lorsque docker compose ps montre chaque conteneur running (ou healthy), ouvrez https://mail.example.com dans un navigateur. L'identifiant admin par défaut est le nom d'utilisateur admin, mot de passe moohoo. Changez ce mot de passe immédiatement dans l'interface d'administration sous Access → Administrators. Si le navigateur avertit NET::ERR_CERT_AUTHORITY_INVALID, le certificat ACME n'a pas encore été émis : consultez l'échec ACME ci-dessous avant de supposer qu'il est cassé ; un certificat auto-signé provisoire est normal pendant la première minute ou deux.
Étape 6 : Ajouter un domaine, une boîte mail et publier DKIM
Dans l'interface d'administration, ouvrez la page Mail Setup (Configuration → Mail Setup), et sous l'onglet Domains cliquez sur Add domain et saisissez example.com. Ensuite, sous Mailboxes, Add mailbox pour créer you@example.com avec un mot de passe. C'est déjà une boîte mail fonctionnelle joignable en IMAP.
Maintenant la clé DKIM. Allez dans Configuration → ARC/DKIM keys ; mailcow a peut-être déjà généré une clé quand vous avez ajouté le domaine, et sinon, générez-en une là : choisissez le domaine, gardez le sélecteur dkim, choisissez 2048 bits, et cliquez sur Add. Copiez la longue valeur TXT qu'il affiche et publiez-la ainsi :
dkim._domainkey.example.com. TXT "v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqh...long-key...QAB"La page Domains de mailcow possède un bouton DNS qui liste chaque enregistrement attendu et affiche une coche verte ou une croix rouge selon ce qui est réellement publié. Utilisez-la comme liste de contrôle : passez chaque ligne au vert avant de tester la délivrabilité. Une ligne DKIM rouge après publication signifie généralement que la clé a été découpée incorrectement en morceaux TXT ; une clé de 2048 bits est plus longue que la limite de 255 caractères d'une seule chaîne TXT, alors collez-la comme une seule valeur logique et laissez votre hébergeur DNS la découper en morceaux pour vous.
Étape 7 : Tester la délivrabilité et viser le 10/10
Allez sur mail-tester.com, copiez l'adresse aléatoire qu'il affiche, et envoyez-lui un message depuis votre nouvelle boîte : connectez-vous au webmail SOGo sur https://mail.example.com/SOGo et envoyez depuis là. Puis cliquez sur « Then check your score ».
Visez 10/10. Les pénalités courantes et leurs causes :
- SPF non aligné : votre enregistrement
MX/SPF est manquant ou l'IP d'envoi n'est pas couverte. Revérifiez le TXT SPF. - La signature DKIM ne se vérifie pas : le TXT
dkim._domainkeyest manquant, encore en propagation, ou déformé. C'est l'oubli le plus fréquent. - Pas de PTR / PTR incohérent : le DNS inverse ne résout pas vers
mail.example.com. Corrigez chez le fournisseur. - Listé sur une liste de blocage : la réputation antérieure de votre IP. Demandez le retrait de la liste ou une IP plus propre.
N'envoyez pas de vrai courrier à Gmail ou Outlook tant que ceci n'affiche pas 10/10. Un score faible plus une IP neuve, c'est ainsi que votre domaine se fait signaler dès le premier jour.
Étape 8 : Connecter un vrai client mail
Pointez Thunderbird, Apple Mail ou votre téléphone vers le serveur avec ces réglages. L'hôte serveur est mail.example.com pour tous :
- IMAP : port 993, SSL/TLS (ou 143 avec STARTTLS)
- Soumission SMTP : port 465, SSL/TLS (ou 587 avec STARTTLS)
- Nom d'utilisateur : l'adresse complète,
you@example.com - Mot de passe : le mot de passe de la boîte que vous avez défini
N'envoyez jamais le courrier client via le port 25 : ce port est réservé au serveur-à-serveur, mailcow n'y propose pas de soumission authentifiée, et un client qui le vise est refusé. Si un client signale Relay access denied, c'est qu'il essaie d'envoyer sur le 25 ou sans authentification ; basculez-le sur 465 ou 587 avec les identifiants de votre boîte.
Étape 9 : Sauvegarder ce qui compte vraiment
mailcow fournit un script de sauvegarde qui capture chaque volume avec état. Exécutez-le vers un disque externe ou un montage distant :
sudo MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \
./helper-scripts/backup_and_restore.sh backup allall capture six choses, et en perdre une seule perd des données : vmail (les boîtes mail elles-mêmes), crypt (les clés qui déchiffrent vmail, inutilisable sans elles), mysql (la base MariaDB contenant domaines, utilisateurs, alias et réglages), redis (l'état de la file et du cache), rspamd (le spam/ham appris), et postfix (la file de courrier). Il s'exécute dans un conteneur d'aide qui écrit des archives compressées, les sauvegardes restent donc cohérentes même pendant que la stack tourne. Automatisez-le avec une tâche cron nocturne, et ajoutez --delete-days 14 pour élaguer les anciens jeux. La restauration est le même script avec restore, qui liste les instantanés et vous laisse choisir ce que vous ramenez. Une sauvegarde que vous n'avez jamais testée en restauration est un espoir, pas une sauvegarde : faites un essai à blanc sur un VPS de test.
Étape 10 : Mettre à jour selon un calendrier
mailcow se met à jour via son propre script, qui récupère le nouveau code, migre mailcow.conf, prérécupère les images, et redémarre les conteneurs dans l'ordre :
cd /opt/mailcow-dockerized
sudo ./update.sh --check # reports whether an update exists, changes nothing
sudo ./update.sh # applies itSauvegardez d'abord (étape 9), car une migration de schéma est difficile à inverser. Les mises à jour arrivent souvent et incluent des correctifs de sécurité pour des démons exposés à Internet, ne laissez donc pas un serveur mail dériver pendant des mois. Si une mise à jour laisse un jour un conteneur en mauvais état, sudo docker compose logs --tail=50 <service>-mailcow nomme le démon qui n'a pas réussi à revenir.
Une note sur le durcissement
mailcow exécute son propre service netfilter (netfilter-mailcow) qui bannit les IP qui matraquent les ports mail et webmail, le côté mail est donc défendu d'emblée. Cela ne couvre pas SSH sur l'hôte lui-même, qui reste exposé et continue de subir des attaques par force brute : associez cette installation à Fail2ban qui surveille le journal d'authentification SSH et à une connexion par clé uniquement. Gardez l'interface d'administration mailcow derrière un mot de passe fort et, idéalement, hors de l'Internet public ou derrière un VPN.
Modes de défaillance, avec les chaînes exactes
Le courrier reste en file et n'est jamais livré. Exécutez sudo docker compose exec postfix-mailcow postqueue -p, ou lisez la file de courrier de l'interface d'administration ; les entrées restent différées avec :
status=deferred (connect to gmail-smtp-in.l.google.com[142.250.x.x]:25: Connection timed out)C'est le port 25 sortant bloqué par votre fournisseur (étape 1). Aucune configuration ne le corrige : ouvrez un ticket. Ce n'est ni le DNS ni le TLS ; l'indice est le mot timed out face à un MX distant sur le port 25.
Gmail marque tout comme spam, ou le rejette. Ouvrez le message dans Gmail, « Afficher l'original », et lisez les résultats d'authentification. dkim=fail ou dkim=none signifie que votre TXT dkim._domainkey est manquant, déformé, ou pas encore propagé : republiez exactement ce qu'affiche la page ARC/DKIM et attendez le TTL. spf=fail signifie que les enregistrements SPF/MX ne couvrent pas votre IP. L'alignement est primordial ; une seule vérification qui échoue suffit à atterrir dans les spams.
Rejeté par les grands fournisseurs à la connexion. Les rebonds ou les journaux Postfix portent le rejet PTR de Gmail :
550-5.7.25 [10.0.0.10] The IP address sending this message does not have a PTR
550-5.7.25 record setup, or the corresponding forward DNS entry does not match
550 5.7.25 the sending IP. As a policy, Gmail does not accept messages from IPs
550 5.7.25 with missing PTR records.Le code 550 5.7.25 signifie un DNS inverse manquant ou incohérent. Réglez le PTR de votre IP vers mail.example.com chez le fournisseur (étape 2). Le direct (A) et l'inverse (PTR) doivent concorder, et les deux doivent nommer le même hôte sous lequel mailcow salue les autres serveurs.
Le navigateur affiche un avertissement de certificat qui ne disparaît jamais. Le conteneur acme-mailcow n'a pas réussi à obtenir un vrai certificat. Consultez son journal :
sudo docker compose logs acme-mailcow | tail -n 40Une ligne comme Cannot validate any hostnames, skipping Let's Encrypt for 1 hour. ou un échec de défi signifie que le port 80 n'est pas joignable depuis Internet, ou que l'enregistrement A ne pointe pas vers ce serveur. Confirmez que mail.example.com résout vers la machine, ouvrez 80 et 443 à travers tout pare-feu de l'hôte, et assurez-vous que rien d'autre n'occupe ces ports. Après avoir corrigé la cause, redémarrez le client avec sudo docker compose restart acme-mailcow plutôt que d'attendre le délai de repli d'une heure.
FAQ
L'auto-hébergement de l'email en vaut-il vraiment la peine ?
Si vous voulez la propriété des données, des alias illimités et un contrôle total, oui : mailcow vous donne une stack professionnelle pour le prix d'un VPS. Mais la délivrabilité est une corvée permanente : la réputation d'IP, l'alignement DNS et la surveillance des listes de blocage ne finissent jamais vraiment. Pour une adresse professionnelle critique où une seule journée dans le dossier spam de quelqu'un vous coûte cher, un fournisseur géré est le choix pragmatique. Auto-hébergez quand vous préférez le contrôle à la commodité et que vous l'entretiendrez réellement.
Comment savoir si le port 25 sortant est bloqué ?
Exécutez nc -vz -w 5 gmail-smtp-in.l.google.com 25 depuis le serveur. « succeeded! » signifie qu'il est ouvert ; un timed out après la pause signifie que votre fournisseur le bloque. C'est la raison la plus fréquente pour laquelle un serveur auto-hébergé peut recevoir du courrier mais jamais en envoyer, et la seule solution est que votre fournisseur ouvre le port : aucun réglage local ne le change.
Pourquoi mon courrier atterrit-il encore dans le dossier spam de Gmail ?
Presque toujours une chaîne d'authentification cassée. Utilisez « Afficher l'original » dans Gmail et cherchez spf=pass, dkim=pass et dmarc=pass. Un dkim=fail pointe vers un enregistrement TXT dkim._domainkey manquant ou déformé ; un PTR incohérent ou une IP neuve sans historique d'envoi nuisent aussi. Amenez d'abord mail-tester.com à 10/10, puis chauffez l'IP lentement, quelques messages par jour puis davantage, plutôt que de déverser du volume dès le premier jour.
Que dois-je exactement sauvegarder ?
Exécutez backup_and_restore.sh backup all et conservez tout le jeu hors du serveur. Il capture vmail (les boîtes mail), crypt (les clés qui les déchiffrent), la base de données MariaDB (domaines, utilisateurs, alias, réglages), Redis, les données apprises de Rspamd, et la file Postfix. Le volume crypt est celui que les gens négligent : sans lui, la sauvegarde vmail est du texte chiffré illisible. Testez une restauration sur une machine de test au moins une fois.
Puis-je faire tourner mailcow sur un VPS de 2 Go ?
Pas confortablement. generate_config.sh propose de désactiver ClamAV en dessous d'environ 2,5 Gio, et même là Rspamd, ClamAV, Dovecot et MariaDB se disputent la mémoire, vous atteindrez donc le swap et des kills OOM sous toute charge réelle. Considérez 6 Gio plus 1 Gio de swap comme le plancher pour une installation mono-utilisateur stable, et passez à 8 Gio dès que plus de deux personnes en dépendent.