Mailpit est un serveur SMTP factice : vos applications lui envoient leurs mails, il les intercepte et les affiche dans une interface web, sans qu'aucun message ne parte réellement. C'est l'outil qui vous évite d'expédier un « Test 3 » à un vrai client pendant une recette. Ce guide montre comment le lancer avec Docker, vérifier automatiquement en CI qu'un mail transactionnel part bien avec le bon contenu, et simuler une panne SMTP pour éprouver la résilience de votre application. Tout est validé sur la version 1.30.4.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Lancer Mailpit en une poignée de lignes et y brancher n'importe quelle application
- Inspecter un mail : rendu HTML, version texte, en-têtes, et compatibilité avec les clients de messagerie
- Automatiser les vérifications avec l'API REST, dans une suite de tests rejouable en intégration continue
- Simuler une panne du serveur SMTP pour vérifier que votre application ne s'effondre pas
- Éviter les erreurs de configuration qui exposent une instance de test sur Internet
Pourquoi un serveur SMTP de test
Section intitulée « Pourquoi un serveur SMTP de test »Dès qu'une application envoie des mails (confirmation de commande, réinitialisation de mot de passe, alerte de supervision), une question se pose en développement : où partent ces messages ? Trois mauvaises réponses circulent. Les envoyer pour de vrai depuis un poste de développement, au risque d'atteindre une adresse réelle avec un jeu de données de test. Désactiver l'envoi, et ne rien vérifier du tout. Ou détourner tout le trafic vers une boîte personnelle, qui devient vite inutilisable.
Un serveur SMTP de test répond proprement au problème. Il accepte les messages comme le ferait un vrai serveur, puis les stocke au lieu de les distribuer. L'application ne voit aucune différence : elle parle SMTP sur le port qu'on lui indique, reçoit un accusé de réception, et poursuit son travail. Vous, vous consultez les messages dans une interface web.
Mailpit est aujourd'hui le choix par défaut pour ce rôle. Il est écrit en Go, tient dans un binaire unique sans dépendance, consomme quelques mégaoctets de mémoire, et se lance en une commande.
Prérequis
Section intitulée « Prérequis »Le lab tient sur n'importe quel poste :
- Docker et le plugin Compose, dont les bases sont couvertes dans le guide Docker Compose
- Une application capable d'envoyer des mails en SMTP, ou simplement Python pour l'exemple de ce guide
- Quelques notions sur les protocoles de messagerie : SMTP, ports, en-têtes
Lancer Mailpit
Section intitulée « Lancer Mailpit »Mailpit expose deux ports : un port SMTP (1025 par défaut) où les applications déposent leurs messages, et un port web (8025) qui sert à la fois l'interface et l'API REST. Retenir cette séparation évite bien des confusions de configuration.
name: mailpit
services: mailpit: image: axllent/mailpit@sha256:5a49a77c5bdbe7c5474450b4f46348d09949df3695257729c93a30369382d4f6 # v1.30.4 restart: unless-stopped ports: - "8025:8025" # interface web et API REST - "1025:1025" # SMTP environment: # Persister les messages sur disque plutôt qu'en mémoire MP_DATABASE: /data/mailpit.db # Plafond du bac à sable : au-delà, les plus anciens sont purgés MP_MAX_MESSAGES: "500" # Protéger l'interface et l'API MP_UI_AUTH: "admin:admin" volumes: - mail-data:/data healthcheck: test: ["CMD", "/mailpit", "readyz"] interval: 5s timeout: 5s retries: 10
volumes: mail-data:Par défaut, Mailpit garde les messages en mémoire : un redémarrage les efface. La variable MP_DATABASE les écrit dans un fichier, ce qui est préférable dès que le bac à sable est partagé par une équipe. MP_MAX_MESSAGES plafonne la rétention, pour qu'une application bavarde ne remplisse pas le disque.
Lancez la pile, puis vérifiez que le service est prêt :
docker compose up -dcurl -s -o /dev/null -w "%{http_code}\n" -u admin:admin http://localhost:8025/Le code attendu est 200. Sans les identifiants, la même requête renvoie 401 : l'authentification protège l'interface et l'API d'un même mouvement.
Brancher une application
Section intitulée « Brancher une application »Il n'y a rien à installer côté application : il suffit de la faire pointer vers l'hôte et le port SMTP de Mailpit, sans chiffrement ni authentification. Voici un envoi typique en Python, celui d'un mail de réinitialisation de mot de passe.
import smtplibfrom email.message import EmailMessage
def envoyer_reinitialisation(destinataire: str, jeton: str) -> None: message = EmailMessage() message["From"] = "facturation@boutique.local" message["To"] = destinataire message["Subject"] = "Réinitialisation de votre mot de passe" message.set_content( f"Votre lien : https://boutique.local/reset?token={jeton}" ) message.add_alternative( f'<html><body><h1>Réinitialisation</h1>' f'<p><a href="https://boutique.local/reset?token={jeton}">' f'Choisir un nouveau mot de passe</a></p></body></html>', subtype="html", )
with smtplib.SMTP("localhost", 1025) as smtp: smtp.send_message(message)Le message n'ira nulle part : il apparaît immédiatement dans l'interface, sur http://localhost:8025. Chaque envoi de l'application, quel que soit le destinataire, atterrit dans cette boîte unique.

En ouvrant un message, vous accédez à ce qui manque cruellement quand on débogue un mail en aveugle : le rendu HTML, la version texte, les en-têtes bruts, mais aussi deux contrôles que Mailpit effectue pour vous. Le HTML Check note la compatibilité de votre code HTML avec les clients de messagerie du marché (Outlook, Gmail, Apple Mail), ce qui explique pourquoi une mise en page correcte dans un navigateur peut s'effondrer dans une boîte mail. Le Link Check vérifie que les liens du message ne sont pas cassés.

Vérifier automatiquement les mails en CI
Section intitulée « Vérifier automatiquement les mails en CI »Consulter une interface graphique convient au développement. En intégration continue, il faut une vérification automatique, et c'est là que l'API REST de Mailpit prend tout son sens : elle transforme le bac à sable en support de tests.
Les endpoints utiles
Section intitulée « Les endpoints utiles »Quatre appels suffisent à couvrir l'essentiel des besoins. L'authentification passe par les mêmes identifiants que l'interface.
| Besoin | Appel |
|---|---|
| Lister les messages | GET /api/v1/messages |
| Rechercher | GET /api/v1/search?query=subject:Réinitialisation |
| Lire un message complet | GET /api/v1/message/{ID} |
| Vider la boîte | DELETE /api/v1/messages |
La recherche mérite un avertissement. Elle fonctionne par champs (subject:, from:, to:) et se montre sensible à la forme exacte des termes : une requête sur reinitialisation sans accent ne remonte pas un message dont le sujet contient « Réinitialisation ». En test automatisé, préférez donc une recherche par champ explicite, ou récupérez simplement le dernier message de la liste.
Une suite de tests rejouable
Section intitulée « Une suite de tests rejouable »Le schéma est toujours le même : vider la boîte, déclencher l'action qui doit produire un mail, puis interroger l'API pour vérifier que le message existe et qu'il contient ce qu'il faut. Avec pytest, cela donne un test lisible par n'importe quel membre de l'équipe.
import osimport pytestimport requestsfrom envoi_mail import envoyer_reinitialisation
API = os.environ.get("MAILPIT_API", "http://localhost:8025/api/v1")AUTH = ("admin", "admin")
@pytest.fixture(autouse=True)def boite_vide(): """Vide le bac à sable avant chaque test, pour partir d'un état connu.""" requests.delete(f"{API}/messages", auth=AUTH, timeout=5)
def test_le_lien_de_reinitialisation_est_correct(): envoyer_reinitialisation("cliente@boutique.local", "jeton-de-test-123")
messages = requests.get(f"{API}/messages", auth=AUTH, timeout=5).json()["messages"] assert len(messages) == 1
message = requests.get( f"{API}/message/{messages[0]['ID']}", auth=AUTH, timeout=5 ).json() assert message["Subject"] == "Réinitialisation de votre mot de passe" assert "jeton-de-test-123" in message["HTML"]La purge avant chaque test est le point de discipline à ne pas négliger : sans elle, les tests se polluent mutuellement et échouent dès qu'ils tournent dans un ordre différent. Vous pouvez pousser la vérification plus loin en interrogeant GET /api/v1/message/{ID}/html-check, dont le champ Total.Supported donne le pourcentage de clients de messagerie compatibles avec votre HTML : un test peut ainsi refuser une régression de mise en page.
En CI, Mailpit se déclare comme service à côté du job, exactement comme une base de données de test. Le job attend que le port 8025 réponde, lance la suite, et tout disparaît à la fin.
Simuler une panne SMTP
Section intitulée « Simuler une panne SMTP »Une question rarement testée : que fait votre application quand le serveur de mail refuse les messages ? Beaucoup de codes considèrent l'envoi comme infaillible et laissent remonter une exception jusqu'à l'utilisateur, voire perdent la commande en cours.
Mailpit répond à cette question avec son mode chaos, activé par la variable MP_ENABLE_CHAOS. Vous fixez alors une probabilité d'erreur et le code SMTP à renvoyer :
curl -u admin:admin -X PUT http://localhost:8025/api/v1/chaos \ -H "Content-Type: application/json" \ -d '{"Sender": {"ErrorCode": 451, "Probability": 100}}'À partir de cet instant, toute tentative d'envoi se solde par un rejet, et votre application reçoit une vraie erreur SMTP 451. C'est le moment de vérifier qu'elle réessaie, qu'elle journalise l'incident, et surtout qu'elle ne perd pas l'action métier en cours. Remettre Probability à 0 rétablit le fonctionnement normal.
Sécuriser une instance partagée
Section intitulée « Sécuriser une instance partagée »Un Mailpit sur son poste ne présente aucun risque. Une instance posée sur un serveur de recette, en revanche, mérite trois précautions.
L'authentification de l'interface et de l'API n'est pas activée par défaut. Sans MP_UI_AUTH, quiconque atteint le port 8025 lit tous les mails capturés : liens de réinitialisation, jetons, adresses des utilisateurs de test. C'est une fuite de données discrète mais réelle, d'autant que ces messages contiennent souvent des jetons valides.
Le port SMTP ne doit jamais être exposé sur Internet. Un serveur SMTP ouvert est repéré en quelques heures et transformé en relais de spam. La version 1.30.4, publiée en juillet 2026, corrige d'ailleurs deux vulnérabilités qui affectaient précisément les instances exposées publiquement : raison suffisante pour suivre les mises à jour et pour n'ouvrir ces ports que sur un réseau interne.
Enfin, gardez en tête que Mailpit est un outil de test, jamais un composant de production. Il ne délivre aucun message : le jour où une configuration erronée pointe une application de production vers lui, tous les mails de vos clients disparaissent en silence. Nommez donc clairement l'instance et séparez strictement les configurations.
Dépannage
Section intitulée « Dépannage »Les erreurs rencontrées lors de la mise en place de ce guide, et ce qui les corrige.
| Symptôme | Cause | Solution |
|---|---|---|
L'application signale Connection refused | Mauvais port, ou Mailpit joint sur le port web | Utiliser le port SMTP (1025), pas le port web (8025) |
L'interface répond 401 | MP_UI_AUTH est actif | Fournir les identifiants, y compris pour les appels d'API |
| Les messages disparaissent au redémarrage | Stockage en mémoire par défaut | Définir MP_DATABASE sur un volume persistant |
| Une recherche ne remonte rien | Termes accentués ou recherche plein texte trop vague | Chercher par champ (subject:, to:) ou prendre le dernier message |
| Les tests échouent quand ils changent d'ordre | La boîte n'est pas vidée entre les tests | Purger avec DELETE /api/v1/messages avant chaque test |
| L'application ne reçoit jamais d'erreur SMTP | Le mode chaos n'est pas activé au démarrage | Ajouter MP_ENABLE_CHAOS=true puis régler la probabilité par l'API |
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »Les réponses ci-dessous couvrent les questions les plus posées sur Mailpit : sa différence avec MailHog, son usage en CI et les précautions à prendre avant de l'exposer.
axllent/mailpit suffit : elle expose le port 1025 pour le SMTP (là où l'application dépose ses mails) et le port 8025 pour l'interface web et l'API. Deux variables méritent d'être ajoutées : MP_DATABASE pour persister les messages sur disque, car ils sont gardés en mémoire par défaut, et MP_UI_AUTH pour protéger l'accès, l'authentification n'étant pas active par défaut.DELETE /api/v1/messages), déclencher l'action qui doit produire un mail, puis vérifier le message (GET /api/v1/messages puis GET /api/v1/message/{ID}). La purge avant chaque test est indispensable, faute de quoi les tests se polluent entre eux et échouent dès que leur ordre change.MP_ENABLE_CHAOS. Un appel PUT /api/v1/chaos fixe un code d'erreur SMTP (par exemple 451) et une probabilité d'échec. L'application reçoit alors une vraie erreur d'envoi, ce qui permet de vérifier qu'elle réessaie, qu'elle journalise l'incident et surtout qu'elle ne perd pas l'action métier en cours.À retenir
Section intitulée « À retenir »- Mailpit est un serveur SMTP factice : il capture les mails de vos applications au lieu de les distribuer, et les affiche dans une interface web.
- Il remplace MailHog, dont la dernière version date de 2020, avec un binaire Go unique et une empreinte mémoire minuscule.
- Deux ports à ne pas confondre : le SMTP (1025) où l'application dépose, le web (8025) qui sert l'interface et l'API REST.
- Son API REST transforme le bac à sable en support de tests automatisés : vider la boîte, déclencher l'action, vérifier le contenu du mail.
- Le mode chaos simule un serveur SMTP en panne et révèle ce que fait votre application quand l'envoi échoue.
- L'authentification n'est pas active par défaut : sans
MP_UI_AUTH, tous les jetons capturés sont lisibles par qui atteint le port.