Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung E-Mail / Mailserver 24.09.2026 · 9 min Lesezeit Wird überarbeitet

Stalwart Mailserver mit Docker Compose installieren

Stalwart bündelt SMTP, IMAP, JMAP, CalDAV und CardDAV in einem Serverprozess. Die Anleitung zeigt Installation mit Docker Compose (getestet mit 0.16.23 und 0.16.26), Einrichtungsassistent, DNS-Einträge, Absicherung, Backup und typische Fehler.

Mit KI erstellt – redaktionelle Prüfung ausstehend

Werbung Mit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Mehr dazu

Titelbild: Stalwart Mailserver mit Docker Compose, SMTP, IMAP, Spamfilter

Ein klassischer Mailserver besteht aus mehreren Diensten: Postfix, Dovecot, Rspamd, Datenbank und Weboberfläche. Stalwart bündelt SMTP, IMAP, POP3, JMAP, CalDAV, CardDAV und WebDAV in einem einzigen, in Rust geschriebenen Serverprozess mit eigener Weboberfläche. Diese Anleitung zeigt die Installation mit Docker Compose, die Ersteinrichtung, DNS, Absicherung, Backup und Updates.

Voraussetzungen

  • Linux-Server (x86-64 oder ARM64) mit Docker Engine und Compose-Plugin; für wenige Postfächer genügen 2 CPU-Kerne, 4 GB RAM und 20 GB Speicher, je nach Mailvolumen mehr.
  • Eigene Domain mit Zugriff auf die DNS-Einträge.
  • Statische öffentliche IPv4-Adresse mit Reverse-DNS-Eintrag (PTR), den Ihr Anbieter setzt.
  • Port 25 offen in beiden Richtungen. Viele Anbieter sperren ausgehendes SMTP, bis Sie die Freigabe beantragen.
  • Ausgehender Internetzugang beim ersten Start; Stalwart lädt die Weboberfläche dabei nach.
EckdatenWert
Imagestalwartlabs/stalwart:v0.16 (Docker Hub, auch GHCR)
Mailports25 (SMTP), 465/587 (Submission), 993/143 (IMAP), 995/110 (POP3), 4190 (ManageSieve)
Web443 (HTTPS, JMAP, CalDAV, CardDAV), 8080 (Einrichtung ohne TLS, nur lokal)
Volumes/etc/stalwart (Konfiguration), /var/lib/stalwart (Daten bei RocksDB)
Benutzer im ContainerUID 2000 (stalwart)

Schritt 1: Tag wählen und compose.yaml anlegen

Stalwart veröffentlicht latest: Minor-Tags wie v0.16, exakte Patch-Tags wie v0.16.23 und edge (Build des Hauptzweigs). Die Dokumentation empfiehlt für den Produktivbetrieb den Minor-Tag: Er erhält Patches, bleibt aber in einer Serie, denn bei einer Version unter 1.0 können sich Datenformate und Konfigurationsschlüssel zwischen Minor-Versionen ändern. Wer Patches selbst steuern will, nimmt den exakten Tag.

$ sudo mkdir -p /opt/stalwart
$ cd /opt/stalwart

Legen Sie /opt/stalwart/compose.yaml an. Ports ohne Adresse veröffentlicht Docker auf allen Schnittstellen; Port 8080 ist deshalb ausdrücklich an localhost gebunden.

services:
  stalwart:
    container_name: stalwart
    image: stalwartlabs/stalwart:v0.16
    restart: unless-stopped
    env_file:
      - .env
    ports:
      - "25:25"
      - "587:587"
      - "465:465"
      - "143:143"
      - "993:993"
      - "4190:4190"
      - "443:443"
      # Einrichtung ohne TLS: nur lokal
      - "127.0.0.1:8080:8080"
    volumes:
      - stalwart-etc:/etc/stalwart
      - stalwart-data:/var/lib/stalwart

volumes:
  stalwart-etc:
  stalwart-data:

Nicht jeder Port ist nötig. Ein öffentlicher Mailserver braucht 25, 443 und die Clientports Ihrer Nutzer; eine interne Installation kommt oft mit 587 und 993 aus. POP3 (110, 995) ist hier bewusst weggelassen. Für Ports unter 1024 genügt das Standard-Capability-Set von Docker; --cap-add oder privileged sind laut Dokumentation unnötig.

sudo docker compose config --quiet endet ohne Fehlermeldung, sobald in Schritt 2 die .env existiert.

Schritt 2: Administratorzugang festlegen

Ohne weitere Angaben startet Stalwart im Bootstrap-Modus und schreibt ein temporäres Administratorkonto mit Zufallspasswort genau einmal ins Log. Es erscheint als „Temporary administrator account created“ mit Benutzer admin und dem Hinweis, dass das Passwort nur einmal angezeigt wird. Robuster ist ein fest vorgegebenes Passwort über STALWART_RECOVERY_ADMIN; dann erzeugt Stalwart kein temporäres Passwort. Legen Sie die .env neben der compose.yaml an:

sudo tee /opt/stalwart/.env >/dev/null <<'ENV'
STALWART_RECOVERY_ADMIN=admin:IHR_ADMIN_PASSWORT
ENV
$ sudo chmod 600 /opt/stalwart/.env

Ersetzen Sie den Platzhalter durch ein langes Zufallspasswort, etwa aus openssl rand -base64 24, und nehmen Sie die Datei nicht in ein Git-Repository auf. Docker Compose liest die .env als root, die Rechte 600 stören deshalb nicht.

sudo docker compose config | grep STALWART_RECOVERY_ADMIN zeigt die Variable mit Ihrem Wert, ls -l /opt/stalwart/.env zeigt -rw-------.

Schritt 3: Starten und Funktion prüfen

$ cd /opt/stalwart
$ sudo docker compose pull
$ sudo docker compose up -d
$ sudo docker compose logs stalwart

Beim ersten Start meldet der Server, dass keine Konfigurationsdatei vorliegt (Bootstrap-Modus), lädt die Weboberfläche nach und öffnet den Listener auf Port 8080 ohne TLS. Der Container ist nach unter einer Minute healthy; der Healthcheck ist im Image enthalten. Prüfen Sie Zustand, Endpunkte und Version:

$ sudo docker compose ps
$ curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/healthz/ready
$ curl -s http://127.0.0.1:8080/healthz/live
$ sudo docker exec stalwart id
$ sudo docker exec stalwart stalwart --version

Dabei liefert /healthz/ready HTTP 200 und /healthz/live den JSON-Körper {"detail":"OK","status":200,"title":"OK","type":"about:blank"}. Der Wurzelpfad leitet mit 302 auf /account weiter, der Prozess läuft als uid=2000(stalwart). Binden Sie beide Healthz-Endpunkte in Ihr Monitoring ein, denn der Containerstatus allein erkennt keinen hängenden Dienst.

sudo docker compose ps zeigt healthy, /healthz/ready liefert 200, und stalwart --version nennt eine 0.16er-Version.

Schritt 4: Ersteinrichtung im Assistenten

Öffnen Sie von Ihrem Arbeitsplatz einen SSH-Tunnel und rufen Sie http://127.0.0.1:8080/admin im Browser auf. Melden Sie sich mit admin und dem Passwort aus Schritt 2 an; der Assistent startet automatisch.

$ ssh -L 8080:127.0.0.1:8080 admin@mail.example.com

Der Assistent umfasst fünf Seiten in dieser Reihenfolge:

  • Server identity: öffentlicher Hostname (etwa mail.example.com, muss per DNS auf den Server zeigen) und Standarddomain. Lassen Sie „Automatically obtain TLS certificate“ und „Generate email signing keys“ (DKIM) aktiviert.
  • Storage: Standard ist RocksDB auf lokaler Platte, für einen einzelnen Server die empfohlene Wahl.
  • Account directory: „Internal Directory“ für die einfachste Installation; LDAP, OIDC oder SQL für ein bestehendes Verzeichnis.
  • Logging: Wählen Sie im Container „Console“, damit Docker die Logs erfasst.
  • DNS management: manuell oder automatisch über einen unterstützten DNS-Anbieter.

Die letzte Seite zeigt E-Mail-Adresse und Passwort des dauerhaften Administrators genau einmal; speichern Sie beides im Passwortmanager. Bei manueller DNS-Verwaltung holt Stalwart das TLS-Zertifikat per TLS-ALPN-01, Let's Encrypt muss den Server dann direkt auf Port 443 erreichen. Nach dem Assistenten erscheint die Seite „Setup complete“ mit Adresse und Passwort sowie dem Hinweis, Stalwart neu zu starten. Starten Sie danach neu:

$ sudo docker compose restart stalwart
Stalwart-Einrichtungsassistent Schritt 1 von 5 mit Server-Hostname, Standarddomain und Schaltern für TLS-Zertifikat und DKIM-Schlüssel
Erste Seite des Assistenten: Hostname, Standarddomain, TLS-Zertifikat und DKIM.
Stalwart-Abschlussseite „Setup complete“ mit E-Mail-Adresse des Administrators und maskiertem Passwort
Abschlussseite des Assistenten, das Passwort ist hier unkenntlich gemacht.

Die Anmeldung unter https://mail.example.com/admin mit dem dauerhaften Administrator gelingt; direkt nach dem Neustart kann für ein bis zwei Minuten noch ein selbstsigniertes Ersatzzertifikat erscheinen.

Schritt 5: DNS-Einträge setzen

Ob Mail ankommt, entscheiden sauberes DNS und die Reputation der IP. Stalwart erzeugt die benötigten Einträge selbst: In der Weboberfläche unter Management, Domains wählen Sie unter Domains, Domains im Aktionsmenü der Domain „View Zone File“ und übertragen die Einträge zu Ihrem DNS-Anbieter.

EintragZweck
MXWelcher Host Mail für Ihre Domain annimmt; zeigt auf einen Hostnamen, nie auf eine IP
SPF (TXT)Welche Hosts für Ihre Domain senden dürfen
DKIM (TXT)Öffentlicher Schlüssel zur Prüfung der Signatur ausgehender Mails
DMARC (TXT)Umgang mit Mails, die SPF und DKIM nicht bestehen, und Berichtsadresse; zuerst beobachten, dann verschärfen
PTRReverse-DNS der IP auf den Mailserver-Hostnamen, gesetzt beim Anbieter
Stalwart-Domainverwaltung mit der Zonendatei: DKIM-, SPF-, MX-, DMARC-, SRV- und MTA-STS-Einträge
Von Stalwart erzeugte Zonendatei der Domain mit allen nötigen DNS-Einträgen.

Eine frische Rechenzentrums-IP landet anfangs oft im Spamordner, auch wenn alles korrekt eingetragen ist. Ob Ihr Anbieter ausgehendes SMTP sperrt, prüfen Sie vom Host aus:

$ timeout 10 bash -c 'exec 3<>/dev/tcp/aspmx.l.google.com/25' && echo offen || echo blockiert

dig +short MX example.com nennt Ihren Mailhost, dig +short TXT example.com zeigt SPF, dig +short -x IHRE-IP liefert den Hostnamen des Mailservers, und der Porttest meldet offen.

Schritt 6: Einrichtungszugang schließen

Sobald https://mail.example.com/admin erreichbar ist und der dauerhafte Administrator funktioniert, empfiehlt die Dokumentation, den HTTP-Einrichtungslistener zu deaktivieren. Entfernen Sie dazu STALWART_RECOVERY_ADMIN aus der .env oder löschen Sie den HTTP-Listener in der Weboberfläche und erstellen den Container neu. Entfernen Sie zusätzlich die Portzeile für 8080: Der Listener auf Port 8080 antwortet nach dem Entfernen der Variable und --force-recreate weiterhin (/healthz/ready liefert 200).

$ cd /opt/stalwart
$ sudo sed -i '/^STALWART_RECOVERY_ADMIN=/d' .env
$ sudo docker compose up -d --force-recreate

Steht Stalwart hinter einem Reverse Proxy oder ist Port 443 auf dem Host anders gemappt, setzen Sie STALWART_PUBLIC_URL (etwa https://mail.example.com:8443) in der .env, damit Discovery-Dokumente die richtige Adresse veröffentlichen. SMTP und IMAP laufen nicht über einen HTTP-Proxy, sondern direkt an den Container.

Die Anmeldung mit dem temporären Konto über den Tunnel auf http://127.0.0.1:8080/admin gelingt nicht mehr, und nach dem Entfernen der Portzeile liefert sudo ss -tlnp | grep 8080 keine Ausgabe mehr.

Schritt 7: Backup und Update

Sichern Sie beide Volumes bei gestopptem Container, sonst erfasst die Sicherung womöglich einen halb geschriebenen Zustand der RocksDB. Compose stellt den Volumes den Projektnamen voran, prüfen Sie die Namen mit sudo docker volume ls | grep stalwart.

cd /opt/stalwart
sudo docker compose stop stalwart
sudo docker run --rm -v stalwart_stalwart-etc:/src:ro -v /backup:/dst alpine \
  tar czf /dst/stalwart-etc-$(date +%F).tar.gz -C /src .
sudo docker run --rm -v stalwart_stalwart-data:/src:ro -v /backup:/dst alpine \
  tar czf /dst/stalwart-data-$(date +%F).tar.gz -C /src .
sudo docker compose start stalwart

Für ein Update lesen Sie die Release Notes, sichern wie oben und ziehen das neue Image. Mit dem Minor-Tag v0.16 erhalten Sie dabei den neuesten Patch der Serie; ein Wechsel auf eine neue Minor-Serie ist eine bewusste Änderung des Tags. Ein Downgrade ist nicht zugesichert, Ihr Rückweg ist das Backup.

$ sudo docker compose pull
$ sudo docker compose up -d
$ sudo docker exec stalwart stalwart --version
$ sudo docker compose logs --tail 200 stalwart

tar tzf /backup/stalwart-data-$(date +%F).tar.gz | head listet Dateien, nach dem Update nennt stalwart --version die neue Patch-Version, und sudo docker compose ps zeigt healthy.

Typische Fehler

Log meldet, DNSSEC könne nicht validiert werden, DANE deaktiviert

Standard-Resolver validiert kein DNSSEC. DNSSEC-validierenden Resolver mit TCP-Zugang verwenden; sudo docker exec stalwart cat /etc/resolv.conf zeigt den aktuellen.

Temporäres Passwort verloren

Wird nur einmal ins Log geschrieben. STALWART_RECOVERY_ADMIN setzen, sudo docker compose up -d --force-recreate.

Weboberfläche fehlt oder bleibt leer

Kein ausgehender Internetzugang beim Erststart. Ausgehendes HTTPS freigeben, Container neu starten.

Mail bleibt in der Warteschlange

Anbieter sperrt Port 25. Freigabe beantragen oder Smarthost nutzen.

Fehler beim Anlegen der Konfiguration

Bind-Mount gehört nicht UID 2000. sudo chown -R 2000:2000 auf die Hostverzeichnisse, neu starten.

Start scheitert nach Härtung

cap_drop: NET_BIND_SERVICE gesetzt. Capability nicht entfernen, das Standard-Set genügt.

Zertifikatswarnung nach dem Assistenten

Let's-Encrypt-Zertifikat noch nicht ausgestellt. Ein bis zwei Minuten warten; Port 443 und DNS prüfen.

Häufige Fragen

Benannte Volumes oder Bind-Mounts?

Benannte Volumes erhalten automatisch passende Rechte. Bei Bind-Mounts müssen die Hostverzeichnisse laut Dokumentation vor dem ersten Start UID 2000 gehören, etwa mit sudo chown -R 2000:2000 /opt/stalwart/etc /opt/stalwart/data.

Reicht RocksDB oder brauchen Sie PostgreSQL?

Für einen einzelnen Server empfiehlt Stalwart RocksDB. PostgreSQL, MySQL, S3 oder Redis lohnen sich bei großen Installationen oder mehreren Knoten und lassen sich laut Dokumentation später über die Weboberfläche umstellen. Bei rein externen Speichern bleibt das Datenvolume leer.

Wie entfernen Sie Stalwart vollständig?

sudo docker compose down entfernt den Container, die Daten bleiben. sudo docker compose down -v löscht zusätzlich beide Volumes und damit alle Postfächer und die Konfiguration unwiderruflich. Führen Sie das nur mit geprüftem Backup aus.

Fazit

Stalwart ersetzt einen ganzen Stapel klassischer Maildienste durch einen Container mit Weboberfläche und Assistent, der nach unter einer Minute läuft. Die eigentliche Arbeit liegt wie bei jedem Mailserver außerhalb des Containers: feste IP mit PTR, freigegebener Port 25, vollständige DNS-Einträge und ein geschlossener Einrichtungszugang. Planen Sie dazu Backups bei gestopptem Dienst und Updates innerhalb der Minor-Serie ein.

Weiterführende Anleitungen und Quellen

StalwartMailserverDocker ComposeSMTPIMAPJMAPSelfhostingDNS