Mailpit est un serveur SMTP. On peut lui envoyer des e-mails mais, par défaut, il ne les transmet jamais aux destinataires. Il les stocke et les restitue via une interface Web.
On l’utilise généralement en environnement de développement ou en test d’intégration.
|
Mailpit est un successeur de MailHog, qui n’est plus maintenu. |
Docker
Le projet a démarré en 2022. Comme MailHog, il est fait en Go. Contrairement à MailHog, il est activement maintenu, avec des versions publiées très régulièrement.
L’auteur publie une image Docker officielle : axllent/mailpit.
Elle est construite sur Alpine et elle est aussi disponible sur ghcr.io/axllent/mailpit.
docker run --publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025 --detach axllent/mailpit
Le port 1025 est utilisé pour l’envoi de messages en SMTP. Le port 8025 est utilisé pour consulter les messages via l’interface Web.
Mailpit embarque aussi un serveur POP3 (port 1110), désactivé tant qu’on ne lui a pas fourni de fichier d’authentification.
Docker Compose
smtp:
image: axllent/mailpit
ports:
- "127.0.0.1:2025:1025"
- "127.0.0.1:8025:8025"
Authentification SMTP
Si l’application s’authentifie auprès du serveur SMTP, il faut demander à Mailpit d’accepter n’importe quel couple utilisateur / mot de passe.
Et comme la connexion n’est pas chiffrée, il faut aussi accepter l’authentification PLAIN ou LOGIN en clair.
smtp:
image: axllent/mailpit
environment:
MP_SMTP_AUTH_ACCEPT_ANY: 1
MP_SMTP_AUTH_ALLOW_INSECURE: 1
Sans ça, l’envoi échoue dès que le client tente de s’authentifier.
JUnit / Testcontainers
Ça peut aussi se démarrer avec Testcontainers pour des tests d’intégration automatisés.
Il n’y a pas de module dédié, un GenericContainer fait très bien l’affaire.
Mailpit expose un endpoint /readyz, qu’on peut utiliser pour attendre que le conteneur soit prêt.
GenericContainer<?> smtp = new GenericContainer<>("axllent/mailpit")
.withExposedPorts(1025, 8025)
.waitingFor(Wait.forHttp("/readyz").forPort(8025));
smtp.start();
int smtpPort = smtp.getMappedPort(1025);
String apiUrl = "http://%s:%d/api/v1".formatted(smtp.getHost(), smtp.getMappedPort(8025));
API
Mailpit expose ses services en API Web. Les principales opérations sont les suivantes :
-
Lire tous les messages :
GET /api/v1/messages -
Lire un message :
GET /api/v1/message/{id} -
Lire la source brute d’un message :
GET /api/v1/message/{id}/raw -
Rechercher des messages :
GET /api/v1/search?query=… -
Supprimer tous les messages :
DELETE /api/v1/messages -
Supprimer des messages :
DELETE /api/v1/messages, avec la liste des identifiants dans le corps de la requête
Attention, la lecture d’un message se fait sur /message (au singulier), alors que la liste et la suppression se font sur /messages (au pluriel).
curl -X DELETE http://localhost:8025/api/v1/messages \
--header 'Content-Type: application/json' \
--data '{"IDs": ["4oRBnPtCXgAqZniRhzLNmS"]}'
Pour les tests, l’identifiant latest est très pratique : il désigne le dernier message reçu.
curl -s http://localhost:8025/api/v1/message/latest | jq --raw-output .Subject
Le piège de MailHog a disparu : l’API déclare bien un type MIME application/json.
Il n’y a donc plus besoin de configurer un convertisseur spécifique côté client.
Par exemple, avec RestClient de Spring Framework, ça fonctionne directement.
RestClient restClient = RestClient.create(apiUrl);
Map<?, ?> message = restClient.get()
.uri("/message/latest")
.retrieve()
.body(Map.class);
assertThat(message.get("Subject")).isEqualTo("Bienvenue");
La documentation complète de l’API est disponible directement dans Mailpit, sur http://localhost:8025/api/v1/.
Avancé
Stockage des messages
Mailpit stocke les messages dans une base SQLite.
Par défaut, c’est un fichier temporaire, supprimé à l’arrêt de Mailpit.
Il peut être rendu persistant grâce au paramètre --database ou à la variable d’environnement MP_DATABASE.
Par ailleurs, Mailpit ne garde que les 500 derniers messages.
Cette limite peut être modifiée avec le paramètre --max ou la variable d’environnement MP_MAX_MESSAGES (0 pour supprimer la limite).
Stockage en fichier
docker run --name mailpit --rm --detach \
--publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025 \
--volume $(pwd)/data:/data \
--env MP_DATABASE=/data/mailpit.db \
axllent/mailpit
Au redémarrage, Mailpit recharge les messages présents dans le fichier.
Stockage distant
Mailpit ne sait pas utiliser MongoDB, contrairement à MailHog. Pour une base partagée en réseau, il supporte rqlite, une base distribuée basée sur SQLite. Il suffit de passer l’URL HTTP du serveur rqlite à la place du chemin de fichier.
docker network create mail
docker run --network mail --name mp-rqlite --rm --detach rqlite/rqlite
docker run --network mail --name mailpit --rm --detach \
--publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025 \
--env MP_DATABASE=http://mp-rqlite:4001 \
axllent/mailpit
Si la base rqlite est protégée, les identifiants se passent dans l’URL : http://<user>:<password>@<host>:<port>.
Et si plusieurs instances de Mailpit partagent la même base, chacune doit avoir son propre identifiant, avec le paramètre --tenant-id ou la variable d’environnement MP_TENANT_ID.
Envoi réel des e-mails
On a vu en introduction que Mailpit ne transmettait jamais les messages aux destinataires.
C’est vrai dans la configuration par défaut, mais il peut aussi fonctionner en relais, grâce au paramètre --smtp-relay-config ou à la variable d’environnement MP_SMTP_RELAY_CONFIG.
La première étape est de définir un fichier de configuration, en YAML cette fois.
host: smtp.example.com
port: 587
starttls: true
auth: plain
username: noreply@example.com
password: change-me
allowed-recipients: '@example\.com$'
Contrairement à MailHog, il n’y a qu’un seul serveur de relais, donc pas besoin de lui donner un nom.
Le paramètre allowed-recipients est une expression régulière qui limite les destinataires autorisés.
C’est une sécurité appréciable pour éviter d’envoyer par erreur un e-mail de test à un vrai client.
Puis on rend le fichier disponible depuis le conteneur, via un volume.
docker run --name mailpit --rm --detach \
--publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025 \
--volume $(pwd)/relay.yaml:/conf/relay.yaml \
--env MP_SMTP_RELAY_CONFIG=/conf/relay.yaml \
axllent/mailpit
Ensuite, on peut libérer (release) les messages depuis l’interface graphique.
L’API peut aussi être utilisée avec un POST sur /api/v1/message/{id}/release, en passant la liste des destinataires.
curl -X POST http://localhost:8025/api/v1/message/latest/release \
--header 'Content-Type: application/json' \
--data '{"To": ["alice@example.com"]}'
Enfin, avec le paramètre --smtp-relay-all ou la variable d’environnement MP_SMTP_RELAY_ALL, Mailpit relaie automatiquement tous les messages reçus, tout en en gardant une copie.