Skip to content

Instantly share code, notes, and snippets.

@jonaaix
Last active August 5, 2025 09:11
Show Gist options
  • Select an option

  • Save jonaaix/fc2709b595fb962151aae19c2e906771 to your computer and use it in GitHub Desktop.

Select an option

Save jonaaix/fc2709b595fb962151aae19c2e906771 to your computer and use it in GitHub Desktop.
Setup Stalwart SMTP Relay with flexible domain authorization

Setup Guide: Stalwart als skalierbarer SMTP-Relay für SaaS

Dieses Dokument beschreibt die Einrichtung eines Stalwart Mail-Servers als reinen SMTP-Relay. Das Setup ist darauf ausgelegt, E-Mails sowohl von eigenen Domains als auch im Namen von Kunden einer SaaS-Anwendung (Whitelabeling) zu versenden.

Architektur-Ziele:

  • Sicherheit: Kein offenes Relay, nur authentifizierter Versand. Sichere HTTPS-Verbindung für die Admin-Oberfläche.
  • Zustellbarkeit: Maximale Reputation durch korrekte SPF-, DKIM- und DMARC-Konfiguration.
  • Skalierbarkeit: Einfaches Hinzufügen von Kundendomains ohne Server-Neukonfiguration.
  • Verwaltbarkeit: Einsatz von Docker Compose und einer Caddy-gesicherten Web-UI.

Schritt 1: DNS-Konfiguration (Grundlagen)

Die korrekte DNS-Konfiguration ist entscheidend. Alle folgenden Einträge werden bei Ihrem Domain-Provider verwaltet.

1.1 A-Records

Wir benötigen A-Records für den Mail-Server selbst und für die Admin-Oberfläche. Beide zeigen auf dieselbe IP.

  • Für den Mail-Server:
    • Typ: A
    • Name: mx
    • Domain: mydomain.de
    • Wert: DEINE_SERVER_IP
  • Für die Admin-Oberfläche:
    • Typ: A
    • Name: admin.mx
    • Domain: mydomain.de
    • Wert: DEINE_SERVER_IP

1.2 DMARC-Record

Der DMARC-Eintrag für die Hauptdomain schützt diese und alle Subdomains.

  • Typ: TXT
  • Name: _dmarc
  • Domain: mydomain.de
  • Wert: v=DMARC1; p=none; rua=mailto:[email protected] (Hinweis: p=none ist eine sichere Start-Richtlinie. Das Postfach für die Berichte muss existieren.)

1.3 SPF-Records für eigene Sende-Domains

Jede Domain/Subdomain, die zum Senden verwendet wird, benötigt einen SPF-Record, der unseren Mail-Server autorisiert.

  • Domain mydomain.de:
    • Typ: TXT
    • Name: @
    • Wert: v=spf1 a:mx.mydomain.de -all
  • Domain news.mydomain.de:
    • Typ: TXT
    • Name: news
    • Wert: v=spf1 a:mx.mydomain.de -all
  • Domain info.mydomain.de:
    • Typ: TXT
    • Name: info
    • Wert: v=spf1 a:mx.mydomain.de -all

(Hinweis: Kundendomains werden später über include:mydomain.de autorisiert.)

1.4 DKIM-Records

Die DKIM-Records werden in einem späteren Schritt generiert und dann hier eingetragen.


Schritt 2: Verzeichnisstruktur und Docker Compose

Wir erstellen eine saubere Ordnerstruktur für unser Projekt, inklusive einer Datei für Caddy.

mkdir stalwart-mail
cd stalwart-mail
mkdir -p config data
touch compose.yml config/config.toml

Die compose.yml definiert nun beide Dienste, Stalwart und Caddy, in einem gemeinsamen Netzwerk.

# compose.yml
services:
    stalwart:
        container_name: stalwart-mail
        image: stalwart-mail/stalwart-mail:latest
        restart: unless-stopped
        ports:
            - "587:587"
            - "465:465"
        volumes:
            - ./config:/opt/stalwart
            - ./data:/var/lib/stalwart
        networks:
            - main-proxy
        labels:
            caddy: admin.mx.mydomain.de
            caddy.reverse_proxy: "{{upstreams 8080}}"

networks:
    main-proxy:
        external: true

Schritt 4: Initialer Start & Admin-Benutzer anlegen

  1. Container starten:

    docker compose up -d && docker compose logs -f

    (Caddy wird beim ersten Start das SSL-Zertifikat für admin.mx.mydomain.de anfordern.)

  2. Admin-Benutzer:

    Admin benutzer wird beim ersten Start in den logs angezeigt.


Schritt 5: DKIM für eigene Domains einrichten

Jetzt generieren wir die DKIM-Schlüssel für unsere eigenen Domains und fügen die öffentlichen Schlüssel unserem DNS hinzu.

  1. Schlüssel generieren:

    docker compose exec stalwart stalwart-cli account dkim-key add mydomain.de --selector stalwart
    docker compose exec stalwart stalwart-cli account dkim-key add news.mydomain.de --selector stalwart
    docker compose exec stalwart stalwart-cli account dkim-key add info.mydomain.de --selector stalwart
  2. DNS-Einträge erstellen: Fügen Sie für jeden der drei Befehle den ausgegebenen Public-Key als TXT-Record im DNS hinzu.

    • Für stalwart._domainkey.mydomain.de
    • Für stalwart._domainkey.news.mydomain.de
    • Für stalwart._domainkey.info.mydomain.de

Schritt 6: Onboarding-Prozess für Kundendomains

Der Server ist nun bereit, E-Mails für jede Domain zu signieren, für die ein DKIM-Schlüssel hinterlegt ist. Für SaaS-Kunden muss folgender Prozess in der Anwendung implementiert werden:

  1. Schlüsselgenerierung: Die App führt für die Kundendomain den Befehl stalwart-cli account dkim-key add kunden-domain.com --selector stalwart aus.
  2. Kunden-Anweisungen: Die App zeigt dem Kunden die zwei DNS-Einträge an, die er setzen muss:
    • SPF: Ein TXT-Record, der include:mydomain.de in seinen eigenen SPF-Record aufnimmt.
    • DKIM: Der öffentliche TXT-Record, der im vorherigen Schritt generiert wurde.
  3. Verifizierung: Die App muss regelmäßig prüfen, ob die DNS-Einträge korrekt gesetzt wurden.
  4. Freischaltung: Erst nach erfolgreicher Verifizierung wird das Senden mit der Kundendomain in der App erlaubt.

Schritt 7: Testen

  • Admin-Oberfläche: Rufen Sie https://admin.mx.mydomain.de im Browser auf. Sie sollten die Stalwart-Login-Seite über eine sichere Verbindung sehen.
  • E-Mail-Versand: Verwenden Sie ein Tool wie swaks oder einen E-Mail-Client, um den Versand zu testen.
swaks --server mx.mydomain.de:587 \
      --auth-user [email protected] \
      --auth-password 'YOUR_VERY_SECURE_PASSWORD' \
      --from '[email protected]' \
      --to '[email protected]' \
      --header "Subject: Test" \
      --body "This is a test email."

Nutzen Sie anschließend einen Dienst wie mail-tester.com, um eine Test-E-Mail an die dort angezeigte Adresse zu senden und eine vollständige Analyse Ihrer Konfiguration (SPF, DKIM, DMARC, Blacklists) zu erhalten.


Schritt 8: Reputation, IP-Warm-up und Versandstrategie

Die technische Einrichtung ist nur die halbe Miete. Der langfristige Erfolg hängt von der Sender-Reputation ab.

8.1 IP- vs. Domain-Reputation

  • IP-Reputation: Ist direkt an die IP-Adresse Ihres Servers gebunden. Eine neue IP startet immer mit einer neutralen oder unbekannten Reputation.
  • Domain-Reputation: Ist an Ihre Sende-Domains (mydomain.de, news.mydomain.de, etc.) gebunden. Sie wird durch korrekte Authentifizierung (SPF, DKIM) und gutes Sende-Verhalten aufgebaut.

Ihre gute Domain-Reputation hilft, die Reputation einer neuen IP schneller aufzubauen.

8.2 IP-Warm-up: Die Notwendigkeit des langsamen Starts

Versenden Sie niemals sofort große Mengen von einer neuen IP. Dies wird von Providern wie Gmail und Microsoft als Spam-Verhalten gewertet. Sie müssen die IP "aufwärmen".

  • Problem: Ein laufendes SaaS kann den Versand nicht einfach pausieren oder global drosseln (z.B. bei Passwort-Resets).
  • Lösung: Implementieren Sie eine intelligente Routing-Logik in Ihrer Anwendung. Bei einem Serverwechsel mit neuer IP leiten Sie den Traffic schrittweise um:
    • Methode A (Prozentualer Split):
      • Woche 1: 5% des Traffics über die neue IP, 95% über die alte.
      • Woche 2: 15% des Traffics über die neue IP, 85% über die alte.
      • ...und so weiter, bis 100% erreicht sind.
    • Methode B (Nach E-Mail-Typ):
      • Leiten Sie zuerst nur E-Mails mit hoher Nutzerinteraktion (Passwort-Resets, Willkommens-Mails) über die neue IP.
      • Leiten Sie schrittweise weniger kritische E-Mails (z.B. wöchentliche Reports) um.

8.3 Versandstrategie für große Mengen (z.B. Newsletter)

  • Throttling ist Pflicht: Versenden Sie große Mengen immer gedrosselt (z.B. 500 E-Mails pro Minute). Dies wird am besten in Ihrer Anwendungslogik (z.B. über eine Laravel Queue) gesteuert, nicht direkt im Mailserver.
  • Segmentierung: Versenden Sie zuerst an Ihre aktivsten Nutzer. Das erzeugt positive Signale bei den Providern.
  • Überwachung: Beobachten Sie während des Versands die Bounce- und Spam-Beschwerderaten. Pausieren Sie die Kampagne bei Problemen, um größeren Reputationsschaden zu vermeiden.

Der Schutz Ihrer Sender-Reputation ist ein kontinuierlicher Prozess und entscheidend für die Zustellbarkeit Ihrer E-Mails.

Schritt 9: Validierungsstrategie und Versandprinzipien

Die Qualität Ihrer E-Mail-Listen ist der wichtigste Hebel zum Schutz Ihrer Reputation.

9.1 Validierung durch den realen Geschäftsprozess

  • Double-Opt-in für Neukunden: Die beste Validierung ist die erste Bestätigungs-E-Mail. Schlägt diese als Hard Bounce fehl, ist die Adresse ungültig und darf nie wieder kontaktiert werden. Ein einzelner fehlgeschlagener Opt-in ist unproblematisch und wird erwartet.
  • Keine proaktive Validierung nötig: Sie müssen nicht alle Adressen im System vorab prüfen. Die Validierung geschieht reaktiv, im Moment des ersten Sendeversuchs.

9.2 Umgang mit importierten Kundenlisten (Hochrisiko)

Vertrauen Sie niemals blind importierten Listen. Ein Kunde mit einer schlechten Liste gefährdet die gesamte Plattform.

  • Kontrollierte Validierungs-Kampagne: Die erste E-Mail an eine importierte Liste ist ein Test.
  • Notbremse (Circuit Breaker): Überwachen Sie die Hard-Bounce-Rate dieser ersten Kampagne in Echtzeit. Überschreitet sie einen Schwellenwert (z.B. 4-5%), stoppen Sie den Versand für diesen Kunden sofort automatisch und fordern Sie eine Listenbereinigung.

9.3 Grundsatz: Eine E-Mail pro Empfänger

Auch wenn der Inhalt identisch ist (z.B. bei Newslettern), muss jeder Empfänger eine eigene, an ihn adressierte E-Mail erhalten.

  • Verwenden Sie niemals BCC für Massen-Mails.
  • Grund: Nur so funktionieren Tracking (Öffnungen/Klicks), Abmelde-Links und die Personalisierung korrekt. Das BCC-Verfahren ist ein klassisches Spam-Merkmal und schadet Ihrer Zustellbarkeit massiv.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment