Mailpit

Email testing inbox: catch and inspect the mail your apps send

Mailpit on Cubeship

Mailpit is an email testing tool: an SMTP server that accepts every message it is sent and keeps it, and a web inbox to read them in, with HTML and link checks and an API.

This template installs it on a Cubeship instance as a test inbox for the apps beside it, with its messages kept in a volume.

Nothing is delivered. Every message sent to Mailpit stays in Mailpit, whatever address it is for, so point staging apps at it to see the mail they send without it reaching anyone. Mailpit can relay messages on to a real SMTP server (MP_SMTP_RELAY_*); this template does not configure that.

What it creates

  • mailpit — Mailpit, from axllent/mailpit:v1.31.1, with its web inbox on the domain you choose behind a username and password, SMTP on port 1025, and a volume at /data holding the message database.

It needs Cubeship 0.7.0 or newer.

What you are asked

InputWhat to give
Where the Mailpit inbox answersA domain you control, pointed at your instance.
The username you sign in withAnything without spaces or a colon. admin by default.
The password you sign in withNothing — the instance generates it and shows it once. Keep a copy.
How many messages to keep500 by default. Beyond it the oldest are deleted; 0 keeps them all.

Sending mail to it

SMTP is not on the domain: a domain on Cubeship carries HTTP only. Port 1025 is reachable by apps on the same instance, at Mailpit's internal address, cubeship-mailpit-production-mailpit with the suggested names.

It accepts any username and password, or none, over plain SMTP without TLS. Give an app that host, port 1025, and turn TLS off. For a Ghost app, for example:

mail__transport: SMTP
mail__options__host: cubeship-mailpit-production-mailpit
mail__options__port: "1025"
mail__options__secure: "false"

and whatever mail__options__auth__user and mail__options__auth__pass it already has. Then open the domain and sign in: the messages are there.

The same username and password protect Mailpit's API, at https://<your domain>/api/v1/.

The volume

The app runs as one copy on the machine its volume is on, and a deploy stops it for a few seconds, during which mail sent to it is refused. The messages are in the volume; they are test mail, so backing it up is optional.

To change how many messages are kept, change MP_MAX_MESSAGES on the mailpit app and redeploy. MP_MAX_AGE (like 7d) deletes by age instead, or as well.

Resources

The app is limited to 1 CPU and 512 MiB of memory. Raise limits in template.yaml if you keep many large messages.

What this creates

mailpit

axllent/mailpit:v1.31.1

/data

Volume of mailpit

template.yaml
# yaml-language-server: $schema=https://cubeship.dev/schema/template/v1.json
version: 1
# The first release that keeps a volume's data across deploys.
minCubeship: "0.7.0"
project: mailpit

inputs:
  - key: domain
    type: domain
    label: Where the Mailpit inbox answers
  - key: username
    type: text
    label: The username you sign in with
    help: No spaces and no colon.
    default: admin
    pattern: ^[^\s:]+$
  - key: password
    type: secret
    label: The password you sign in with
    generate: 24
  - key: maxMessages
    type: number
    label: How many messages to keep
    help: The oldest are deleted beyond this. 0 keeps every message.
    default: 500
    min: 0

apps:
  - key: web
    name: mailpit
    image: axllent/mailpit
    tag: v1.31.1
    port: 8025
    # Outside the sign-in, and 200 only once the database answers.
    health: /readyz
    domains:
      - host: ${input.domain}
    volumes:
      - path: /data
    limits: { cpu: 1, memory: 512Mi }
    env:
      MP_DATABASE: /data/mailpit.db
      MP_MAX_MESSAGES: ${input.maxMessages}
      # Basic auth for the web UI and API; plain user:password is accepted.
      MP_UI_AUTH: ${input.username}:${input.password}
      # SMTP on 1025 takes any credentials, or none, without TLS: it is
      # reachable only by apps on the same instance.
      MP_SMTP_AUTH_ACCEPT_ANY: "true"
      MP_SMTP_AUTH_ALLOW_INSECURE: "true"