Skip to content

Messages: MTA Configuration

Messages includes two Postfix-based MTA containers for email handling.

MTA-in (Inbound)

Receives emails via SMTP on port 25 and delivers them to the Django backend through a custom milter.

mta-in:
  image: ghcr.io/suitenumerique/messages-mta-in:main
  user: root   # Required for milter socket permissions
  environment:
    MYHOSTNAME: mx.<domain>
    MDA_API_BASE_URL: http://backend:8000/api/v1.0/
    MDA_API_SECRET: <mda-secret>
    MAX_INCOMING_EMAIL_SIZE: 30000000
  ports: ["25:25"]

Critical: user: root is required. Without it, the milter socket (/var/spool/postfix/milter/delivery.sock) has a permission mismatch and Postfix rejects all incoming email with 451 4.7.1 Service unavailable.

MTA-out (Outbound)

Relays outgoing emails to Brevo. The backend sends to Brevo directly (not through MTA-out) to avoid TLS certificate issues.

mta-out:
  image: ghcr.io/suitenumerique/messages-mta-out:main
  user: root
  entrypoint: ["/bin/sh", "-c", "mkdir -p /var/spool/postfix/etc && chown -R postfix:postfix ... && exec /usr/local/bin/entrypoint.sh"]
  environment:
    MYHOSTNAME: mx.<domain>
    SMTP_USERNAME: messages
    SMTP_PASSWORD: <mta-password>
    SMTP_RELAY_HOST: smtp-relay.brevo.com:587
    SMTP_RELAY_USERNAME: <brevo-username>
    SMTP_RELAY_PASSWORD: <brevo-password>

Two fixes required:

  1. user: root — the entrypoint creates SASL password files in /var/spool/postfix/etc/ which must exist before Postfix starts
  2. Custom entrypoint — creates the directory and fixes ownership before running the original entrypoint

Backend Outbound Path

The Django backend sends outbound emails directly to Brevo, bypassing MTA-out:

MTA_OUT_MODE=relay
MTA_OUT_RELAY_HOST=smtp-relay.brevo.com:587
MTA_OUT_RELAY_USERNAME=<brevo-username>
MTA_OUT_RELAY_PASSWORD=<brevo-password>

This avoids TLS handshake issues with MTA-out's self-signed snakeoil certificate. The MTA-out container still runs for any internal SMTP submission needs.

Firewall

Inbound port 25 must be open:

  • Hetzner Cloud: Add TCP/25 inbound rule in the Cloud Firewall
  • Other VPS: Check provider's firewall settings

Outbound port 25 is NOT needed — all outbound email uses Brevo on port 587.