This project provides a minimal, modern, and debuggable Postfix-based SMTP relay packaged as a Docker container. It is designed to act as a reliable infrastructure component, relaying mail from internal systems to an upstream SMTP server with TLS and SASL authentication.
  • Shell 86%
  • Dockerfile 7.7%
  • Go Template 6.3%
Find a file
Michael Kleger ca02f07242 Version 1.0.1
- Implementierung weg von turgon37/smtp-relay zu einer eigenen moderneren Implementierung
- Kompletter neu Aufbau
- Meldung bei einer Nichtzustellung in ein Zammad Ticket
2026-01-22 18:24:32 +01:00
rootfs Version 1.0.1 2026-01-22 18:24:32 +01:00
.gitignore Version 1.0.1 2026-01-22 18:24:32 +01:00
docker-compose.yml Version 1.0.1 2026-01-22 18:24:32 +01:00
Dockerfile Version 1.0.1 2026-01-22 18:24:32 +01:00
README.md Version 1.0.1 2026-01-22 18:24:32 +01:00
release.sh Version 1.0.1 2026-01-22 18:24:32 +01:00

📬 Postfix SMTP Relay Docker

This project provides a minimal, modern, and debuggable Postfix-based SMTP relay packaged as a Docker container. It is designed to act as a reliable infrastructure component, relaying mail from internal systems to an upstream SMTP server with TLS and SASL authentication.

The image focuses on:

  • correctness
  • clear logging via docker logs
  • predictable sender handling
  • zero open-relay risk

In addition, it includes built-in alerting for delivery and identity failures, with optional integration into Zammad as tickets instead of sending alert emails.


🚀 Features

  • Postfix-based SMTP relay (no mailbox storage)
  • TLS-secured relay to upstream SMTP (port 587)
  • SASL authentication via Docker secrets
  • Forced sender rewriting (prevents upstream rejections)
  • Clean, full mail logs available via docker logs
  • Safe defaults (no open relay)
  • Alerting on delivery failures and identity issues
  • Optional Zammad ticket creation for alerts
  • Designed for internal infrastructure use

🐳 Usage (Docker Compose)

version: "3.9"

services:
  mailrelay:
    build: .
    container_name: mailrelay
    restart: unless-stopped

    ports:
      - "25:25"   # expose only on trusted/internal networks

    environment:
      HOSTNAME_FQDN: "mailrelay.example.local"

      # Fixed sender handling
      DEFAULT_FROM: "user@example.local"
      FORCE_DEFAULT_FROM: "1"

      # Relay target
      RELAY_HOST: "smtp.example.local"
      RELAY_PORT: "587"
      RELAY_TLS: "required"

      # Allowed client networks
      MYNETWORKS: "127.0.0.0/8,10.0.0.0/8,192.168.0.0/16"

      # SASL authentication
      RELAY_SASL_USER: "loginuser@example.local"
      RELAY_SASL_PASS_FILE: "/run/secrets/relay_sasl_pass"

      # Alerting
      ALERT_ENABLED: "1"
      ALERT_MATCH: "invalid_user,sendas_denied,postfix_bounced,postfix_deferred"

      # Zammad integration
      ALERT_ZAMMAD_ENABLED: "1"
      ZAMMAD_BASE_URL: "https://zammad.example.com"
      ZAMMAD_TOKEN_FILE: "/run/secrets/zammad_token"
      ZAMMAD_GROUP: "Support"
      ZAMMAD_STATE: "new"
      ZAMMAD_PRIORITY_ID: "2"   # 1=low, 2=normal, 3=high
      ZAMMAD_CUSTOMER_EMAIL: "mailrelay@example.local"
      ZAMMAD_TAGS: "mailrelay,postfix,alert"

      # Debugging
      DEBUG: "1"

    secrets:
      - relay_sasl_pass
      - zammad_token

    volumes:
      - postfix_spool:/var/spool/postfix
      - postfix_state:/var/lib/postfix
      - /etc/ssl/certs:/etc/ssl/certs:ro

secrets:
  relay_sasl_pass:
    file: ./secrets/relay_sasl_pass.txt
  zammad_token:
    file: ./secrets/zammad_token.txt

volumes:
  postfix_spool:
  postfix_state:

🔐 Secrets

relay_sasl_pass.txt

The secret file must contain only the password (no username):

super-secret-password

The username is configured via RELAY_SASL_USER.

zammad_token.txt

Contains a Zammad API token with permission to create tickets:

zammad-api-token-here

✉️ Sender Handling (Important)

Many SMTP relays reject mails if the sender does not match the authenticated user.

This image enforces a safe default:

  • If DEFAULT_FROM is set → local senders are rewritten to this address
  • Otherwise → fallback to RELAY_SASL_USER

This avoids errors like:

553 5.7.1 Sender address rejected: not owned by user

Sender rewriting is applied only to local / infrastructure senders (e.g. root, daemon, host-based senders).


🚨 Alerting & Monitoring

The container continuously monitors Postfix logs and triggers alerts on actionable failures, including:

  • invalid_user upstream mailbox or identity does not exist
  • sendas_denied sender not permitted to send as this address
  • postfix_bounced delivery permanently failed
  • postfix_deferred delivery temporarily failed

Alert detection is regex-based and operates directly on the Postfix log stream.

Alert Flow

  1. Postfix writes a failure to the log
  2. The internal watcher matches the error
  3. Context (queue ID, sender, recipient, reason) is extracted
  4. A Zammad ticket is created with full diagnostic details

Alerts are throttled to avoid flooding during repeated failures.


🎟 Zammad Ticket Details

Each alert creates an internal Zammad ticket containing:

  • Failure type (e.g. postfix_bounced, ErrorInvalidUser)
  • Affected sender and recipient (best effort)
  • Queue ID
  • Full Postfix error reason
  • Recent log context
  • Fix hints for common identity problems

This makes the relay suitable for production use without silent mail loss.


🧪 Testing the Relay

docker exec -it mailrelay swaks \
  --to admin@example.com \
  --from test@example.com \
  --server 127.0.0.1 \
  --port 25 \
  --header "Subject: Relay test" \
  --data "Hello from mailrelay"

Using sendmail

docker exec -i mailrelay sendmail admin@example.com <<EOF
From: test@example.com
To: admin@example.com
Subject: Relay test

Hello from mailrelay
EOF

⚠️ Do not use sendmail -v unless you want delivery status notifications sent back to you.


📜 Logs

All Postfix logs are written to a file and streamed to stdout:

docker logs -f mailrelay

This makes it possible to audit delivery and alerting via Docker logs alone.


🔒 Security Notes

  • This container is not a public mail server
  • Never expose port 25 to the internet
  • Always restrict MYNETWORKS
  • No mailboxes are stored; this is a pure relay

🧑‍💻 Author

Michael Kleger OneSystems GmbH

https://www.onesystems.ch

mkleger@onesystems.ch


📝 License

MIT License free for commercial and private use.