MailHog

MailHog est un serveur SMTP. On peut lui envoyer des e-mails mais il ne les transmet jamais aux destinataires. Il les garde en mémoire et les restitue via une interface Web.

On l’utilise généralement en environnement de développement ou en test d’intégration.

Docker

Le projet a démarré en 2014. Il est fait en Go. Sa maintenance est au ralenti, plein de tickets sont ouverts et des MR sont soumises. A suivre : fork ?

Les auteurs ont publié une image Docker : mailhog/mailhog. Mais elle n’est pas mise à jour. Il existe plein d’alternatives comme druidfi/mailhog qui est mise à jour régulièrement et est plus petite (8 Mo, from scratch) que l’originale (400 Mo).

docker run --publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025 --detach druidfi/mailhog

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.

Docker Compose

  smtp:
    image: druidfi/mailhog
    ports:
      - "127.0.0.1:2025:1025"
      - "127.0.0.1:8025:8025"

JUnit / Testcontainer

Ça peut aussi se démarrer avec Testcontainer pour des tests d’intégration automatisés.

  GenericContainer<?> smtp = new GenericContainer<>("druidfi/mailhog")
                                        .withExposedPorts(1025, 8025);
  smtp.start();

  int smtpPort = smtp.getMappedPort(1025);

API

MailHog expose ses services en API Web. Les principales opérations sont les suivantes :

  • Lire tous les messages : GET /api/v2/messages

  • Supprimer tous les messages : DELETE /api/v1/messages

  • Lire un message : GET /api/v1/messages/{id}

  • Supprimer un message : DELETE /api/v1/messages/{id}

Il y a un piège dans l’API. Elle produit du contenu au format JSON, pour lequel on attend habituellement un type Mime "application/json". Or MailHog déclare un header "text/json", qui n’est pas compris nativement par tous les clients.

Par exemple, avec RestTemplate de Spring Framework, il faut déclarer ce type spécifiquement.

    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setSupportedMediaTypes(List.of(new MediaType("text", "json")));

    RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory());
    restTemplate.setMessageConverters(List.of(converter));

Avancé

Stockage des messages

Par défaut, les messages sont stockés en mémoire uniquement. MailHog peut être paramétré pour rendre les messages persistants, grâce au paramètre -storage ou à la variable d’environnement MH_STORAGE.

Stockage en fichiers

docker run --name mailhog --rm --detach                                   \
           --publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025    \
           mailhog/mailhog -storage=maildir -maildir-path=/var/mail

Le stockage en répertoire ne fonctionne pas avec l’image druidfi/mailhog car elle est construite from scratch.

Stockage MongoDB 4.x (docker)

docker run --network mail --name mh-mongo                \
           --env MONGO_INITDB_ROOT_USERNAME=mongoadmin   \
           --env MONGO_INITDB_ROOT_PASSWORD=secret       \
           --rm --detach mongo:4

docker run --network mail --name mailhog --rm --detach                    \
           --publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025    \
           mailhog/mailhog -storage=mongodb -mongo-uri=mh-mongo

Ça ne marche pas avec un MongoDB version 5 avec authentification.

Stockage MongoDB 4.x (clever cloud)

# Information à récupérer dans la console de l'add-on
username=kfif5tflgdf4lskfme23
password=lhy5cGkEsVB65r8gt6r
db=bpa2demglxd23se

host=$db-mongodb.services.clever-cloud.com
docker run --name mailhog --rm --detach                                          \
           --publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025           \
           mailhog/mailhog -storage=mongodb                                      \
                           -mongo-uri=$username:$password@$host/$db -mongo-db=$db

J’ai testé avec une base MongoDB de Dev (gratuite), et ça fonctionne sans problème. C’est un vieux add-on, en version 4.0.3.

Il semble que MailHog ne sache pas utiliser MongoDB Atlas, mais je ne sais pas pourquoi. En tout cas, j’ai testé avec un cluster gratuit, en version 5.0, et ça ne fonctionne pas. L’erreur rencontrée est Error connecting to MongoDB: no reachable servers.

Envoi réel des e-mails

L’introduction que MailHog ne transmettait jamais les messages aux destinataires. C’est vrai dans la configuration par défaut, mais ça peut aussi être modifié pour qu’il fonctionne en relais, grâce au paramètre -outgoing-smtp ou à la variable d’environnement MH_OUTGOING_SMTP.

La première étape est de définir un fichier de configuration en JSON.

{
    "ovh": {
        "name": "ovh",
        "host": "smtp.ovh.net",
        "port": "25",
        "email": "noreply@jtips.info",
        "username": "mail@jtips.info",
        "password": "fjzlgre36vqz",
        "mechanism": "PLAIN"
    }
}
la valeur de "name" doit être identique à la clé ("ovh").

Puis on rend le fichier disponible depuis le conteneur, via un volume.

docker run --name mailhog --rm --detach                                          \
           --publish 127.0.0.1:2025:1025 --publish 127.0.0.1:8025:8025           \
           --volume $(pwd)/outgoing.json:/conf/outgoing.json \
           druidfi/mailhog -outgoing-smtp=/conf/outgoing.json

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/messages/{message-id}}/release, en passant le nom du serveur SMTP à utiliser {"name":"ovh"}.

id=$(curl -s http://localhost:8025/api/v2/messages | jq --raw-output .items[0].ID)
curl http://localhost:8025/api/v1/messages/$id/release -X POST --data '{"name": "ovh"}'