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

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.
| Eckdaten | Wert |
|---|---|
| Image | stalwartlabs/stalwart:v0.16 (Docker Hub, auch GHCR) |
| Mailports | 25 (SMTP), 465/587 (Submission), 993/143 (IMAP), 995/110 (POP3), 4190 (ManageSieve) |
| Web | 443 (HTTPS, JMAP, CalDAV, CardDAV), 8080 (Einrichtung ohne TLS, nur lokal) |
| Volumes | /etc/stalwart (Konfiguration), /var/lib/stalwart (Daten bei RocksDB) |
| Benutzer im Container | UID 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/stalwartLegen 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/.envErsetzen 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 stalwartBeim 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 --versionDabei 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.comDer 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

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.
| Eintrag | Zweck |
|---|---|
| MX | Welcher 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 |
| PTR | Reverse-DNS der IP auf den Mailserver-Hostnamen, gesetzt beim Anbieter |

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 blockiertdig +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-recreateSteht 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 stalwartFü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 stalwarttar 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.confzeigt den aktuellen.- Temporäres Passwort verloren
Wird nur einmal ins Log geschrieben.
STALWART_RECOVERY_ADMINsetzen,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:2000auf die Hostverzeichnisse, neu starten.- Start scheitert nach Härtung
cap_drop: NET_BIND_SERVICEgesetzt. 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.


