Mailpit in Docker Compose

services: mailpit: image: axllent/mailpit:v1.31 ports: - "8025:8025" - "1025:1025" volumes: - /path/to/mailpit-data:/data environment: MP_DATABASE: /data/mailpit.db MP_UI_AUTH_FILE: /data/authfile TZ: Europe/London restart: unless-stopped

In Docker Compose, set MP_DATABASE: /data/mailpit.db and mount a volume at /data, or Mailpit keeps its messages in a temporary file that it deletes when it exits. The web UI is on port 8025 and the SMTP server your app sends to is on 1025.

The docker run command, converted

The options example from Mailpit’s Docker documentation, with the tag pinned:

docker run -d \
--name=mailpit \
--restart unless-stopped \
-v /path/to/mailpit-data:/data \
-e MP_DATABASE=/data/mailpit.db \
-e MP_UI_AUTH_FILE=/data/authfile \
-e TZ=Europe/London \
-p 8025:8025 \
-p 1025:1025 \
axllent/mailpit:v1.31
services:
  mailpit:
    image: axllent/mailpit:v1.31
    ports:
      - "8025:8025"
      - "1025:1025"
    volumes:
      - /path/to/mailpit-data:/data
    environment:
      MP_DATABASE: /data/mailpit.db
      MP_UI_AUTH_FILE: /data/authfile
      TZ: Europe/London
    restart: unless-stopped

MP_UI_AUTH_FILE puts the UI and API behind a password, and the file has to exist: without it Mailpit exits with [ui] HTTP password file not found or readable: /data/authfile, and restart: unless-stopped starts it again every few seconds. Each line is user:password, in plain text or hashed (bcrypt, APR1, SHA and others). MP_UI_AUTH: "user1:password1" does the same without a file. TZ sets the zone for Mailpit’s times.

Mail for the app, once Mailpit is up

services:
  mailpit:
    image: axllent/mailpit:v1.31
    ports:
      - "127.0.0.1:8025:8025"
      - "127.0.0.1:1025:1025"
    volumes:
      - ./mailpit-data:/data
    environment:
      MP_DATABASE: /data/mailpit.db
      MP_UI_AUTH_FILE: /data/authfile
      MP_MAX_MESSAGES: 5000
      MP_SMTP_AUTH_ACCEPT_ANY: 1
      MP_SMTP_AUTH_ALLOW_INSECURE: 1
      TZ: Europe/London
    restart: unless-stopped
  app:
    image: example/app:1.0
    environment:
      SMTP_HOST: mailpit
      SMTP_PORT: 1025
    depends_on:
      mailpit:
        condition: service_healthy

There is no healthcheck: to write: the image has its own, which runs /mailpit readyz every 15 seconds, and every second during its first 10, so service_healthy works as it is. The two SMTP_AUTH variables let an app that insists on logging in use any username and password over plain SMTP. Mailpit keeps the newest 500 messages unless MP_MAX_MESSAGES says otherwise.

Gotchas

  • The app sends to mailpit:1025 without TLS. localhost:1025 inside the app’s container is the app itself.
  • If only other services send mail, leave 1025 out of ports; they reach it over the project network anyway.
  • ./mailpit-data is a bind mount, so docker compose down -v leaves the mail in it. Delete the folder to start over.

Try it in the docker run to Compose Converter

compose.yaml

1 service · 1 thing left out

Left out, and why

  • -dCompose starts services in the background already, so this flag needs no key.

Runs in your browser. Nothing you enter is sent anywhere.