Mehrere Bausteine dieser Serie können dich aktiv über Ereignisse informieren – Uptime Kuma bei Downtime, CrowdSec bei Angriffen, Authelia beim Passwort-Reset, Komodo bei Stack-Problemen. Alle haben eines gemeinsam: Sie wollen Mails verschicken. Und keiner dieser Bausteine bringt einen eigenen Mailserver mit.
Wir bauen deshalb einen kleinen, zentralen Helfer: einen Mail-Relay-Container, der Mails von allen Apps annimmt und an einen externen SMTP-Provider weiterleitet. Apps müssen nichts über deinen Provider wissen – sie reichen ihre Mails einfach an mail-relay:2500 durch. Wechselst du später den Anbieter, änderst du eine .env-Datei. Keine App wird angefasst.
Wer mit dem Setup ganz ohne Mail oder mit einer Direkt-Anbindung pro App leben kann, findet das im Anhang. Empfohlen für diese Serie ist der Relay.
Am Ende hast du
- Einen zentralen Mail-Relay (msmtpd) als UI-Defined Stack in Komodo
- SMTP-Credentials nur an einer einzigen Stelle – in der
.envdes Relay-Stacks - Provider-Wechsel (z. B. Netcup ↔ Manitu) in unter einer Minute, ohne App-Anfassen
- Einen erfolgreichen Test-Versand über swaks
- Verstanden, welche Apps in dieser Serie Mail überhaupt brauchen – und welche nicht
Voraussetzungen
- Artikel 02–07 abgeschlossen
- Komodo läuft, Server
vps-homelabist eingebunden und aktiv - Ein Webhosting-Paket mit SMTP-Zugang bei deinem Anbieter (z. B. Netcup oder Manitu)
- Eine Mail-Adresse auf einer Domain, die dort gehostet wird (z. B.
noreply@deine.domain.de) - Zugang zum Server Control Panel deines VPS-Anbieters (siehe Schritt 4 – bei Netcup ist eine Firewall-Anpassung nötig, bevor der Relay funktioniert)
1. Wer braucht Mail in dieser Serie?
Bevor du den Stack baust: Mail ist nicht für jede App ein Muss. Hier eine ehrliche Einschätzung für die Bausteine, die ab Artikel 09 kommen:
| App | Mail nötig? | Wofür? |
|---|---|---|
| Authelia | optional | Passwort-Reset, Setup-Links für neue Nutzer |
| Uptime Kuma | empfohlen | Downtime-Alerts (sonst nur sichtbar in der UI) |
| CrowdSec | optional | Angriffs-Benachrichtigungen |
| Komodo | optional | Stack-Probleme, verfügbare Updates |
| Vaultwarden | optional | Self-Service-Account-Funktionen |
| Immich | nein | Account-Verwaltung läuft manuell |
| Paperless-ngx | nein | Standard-Setup ohne Mail-Import |
| Ghost | ja | Mitgliederverwaltung, Admin-Invites – aber Ghost ist Phase-3-Ausblick |
Wer in der Serie auf Vaultwarden im VPN beschränkt bleibt, kann diesen Artikel überspringen. Sobald aber Uptime Kuma oder Authelia ins Spiel kommen, lohnt sich der Relay.
2. Die drei Architektur-Optionen im Überblick
| Option | Setup-Zeit | Wann sinnvoll? |
|---|---|---|
| Ohne Mail | 0 Min | Test-Umgebung, Apps brauchen keine Mail |
| Direkt-Anbindung | 5 Min/App | 1–2 Apps, schnelles Ausprobieren |
| Zentraler Relay | 15 Min einmalig | 3+ Apps, sauberer Setup-Standard |
Diesen Artikel baust du als Relay-Setup auf. Direkt-Anbindung und „ohne Mail" stehen kompakt im Anhang.
Warum der Relay die Standard-Wahl ist:
- SMTP-Passwort steht nur an einer einzigen Stelle – nicht in jeder App-Compose
- Provider-Wechsel ist eine
.env-Änderung, kein App-Refactor - Apps sehen niemals dein SMTP-Passwort und können es nicht in irgendwelchen Logs ausplaudern
- Du hast einen einzigen Ort, an dem du sehen kannst, ob der Versand klappt (Container-Log)
3. Provider-Wahl: Netcup oder Manitu
Beide bieten Webhosting-Pakete mit SMTP-Zugang ab wenigen Euro pro Monat. Mail-Hosting ist bei beiden in jedem Webhosting-Tarif enthalten.
| Netcup Webhosting | Manitu Webhosting | |
|---|---|---|
| Einstieg | ab 1,99 €/Monat | ab 5,99 €/Monat |
| E-Mail-Adressen (Einstieg) | 100 | 25 |
| Standort | Deutschland | Deutschland (klimaneutral) |
| Stärke | Preis-Leistung, gut wenn du eh schon VPS bei Netcup hast | Sehr stabiler Mailbetrieb, exzellenter Support |
Beide reichen für diese Serie problemlos. Der Relay sendet ein paar Notifications pro Tag, vielleicht ein paar Dutzend bei aktivem Uptime-Monitoring. Da kommt kein Provider auch nur in die Nähe seiner Limits.
Hostname und Port findest du im Kundenbereich
Die konkreten SMTP-Server-Daten unterscheiden sich je nach Anbieter und teilweise je nach Server, auf dem dein Webhosting liegt. Im Netcup-CCP findest du sie unter „Webhosting → Server-Details", bei Manitu unter „Pakete → dein Webhosting → Zugangsdaten". Übernimm die Werte von dort, nicht aus dieser Anleitung.
Beide Provider unterstützen sowohl SSL/TLS auf Port 465 als auch STARTTLS auf Port 587. Im Setup unten zeige ich beide Varianten.
(Affiliate-Links: keine Mehrkosten für dich)
4. Outbound-SMTP am VPS freischalten
Bevor du den Stack baust, ein leicht zu übersehender Punkt: Dein VPS hat möglicherweise zwei Firewall-Schichten. UFW läuft auf dem Server selbst (Artikel 02). Manche Provider – allen voran Netcup – haben aber zusätzlich eine eigene Firewall vor dem VPS, im Server Control Panel (SCP). Diese SCP-Firewall blockiert standardmäßig alle drei ausgehenden SMTP-Ports (25, 465, 587) als Spam-Prävention.
Effekt: Der Mail-Relay-Container startet sauber, der Test schlägt aber mit Operation timed out fehl, ohne dass UFW oder Docker oder die Container-Config einen Hinweis darauf liefern. Deshalb klären wir das hier vor dem Stack-Bau.
4.1 Bei Netcup: SCP-Firewall-Policy entfernen
- https://www.servercontrolpanel.de/ einloggen
- VPS auswählen
- Links: Firewall
- Bei der Policy
netcup Mail blockrechts auf Löschen - Unten rechts auf Speichern
Die Policy enthält drei Regeln (DROP auf Port 25, 465, 587). Bei der Default-Policy lassen sie sich nur gemeinsam entfernen – das ist absichtlich so.
4.2 Bei anderen Providern: Outbound prüfen
Wenn du nicht bei Netcup bist, prüfe einfach kurz, ob outbound zu deinem SMTP-Provider durchgeht. Vom VPS aus:
nc -zv mail.manitu.de 465
Erwartung: Connection to mail.manitu.de ... succeeded!
Falls dort Operation timed out oder ähnliches steht: Provider blockiert den Port. Lösung je nach Anbieter – Hetzner, DigitalOcean, AWS und einige andere sperren standardmäßig Port 25 outbound, lassen aber 465 und 587 offen. Bei Bedarf: Support-Ticket.
4.3 Optional: Port 25 outbound selbst sperren (Spam-Schutz)
Wenn du bei Netcup die ganze netcup Mail block-Policy entfernt hast, ist Port 25 outbound jetzt offen. Du brauchst Port 25 nie – der Relay nutzt ausschließlich 465. Falls dein Server irgendwann kompromittiert wird und Malware Mails versenden will, ist Port 25 der klassische Angriffspunkt (kein Auth nötig).
Selbst nachholen per UFW:
sudo ufw deny out 25/tcp comment 'Spam-Bremse: kein direktes SMTP nach außen'
sudo ufw reload
sudo ufw status verbose
Erwartung: In der Ausgabe taucht eine Zeile auf:
25/tcp DENY OUT Anywhere (Spam-Bremse: kein direktes SMTP nach außen)
Port 465 (und 587) bleiben outbound offen – nur Port 25 ist blockiert. Saubere Lösung, weil die Regel im Server-Setup sichtbar ist und nicht versteckt im SCP-UI lebt.
5. Stack anlegen in Komodo
In Komodo (http://vps:9120 oder über die Tailscale-IP):
- Links Stacks → oben rechts + New Stack
- Name:
mail-relay - Server:
vps-homelab - Stack Type:
UI Defined - Create
Du landest im Stack-Editor. Wir pflegen zwei Dateien: compose.yaml und .env.
6. compose.yaml
Im Compose-Tab:
name: mail-relay
services:
mail-relay:
image: crazymax/msmtpd:latest
container_name: mail-relay
restart: unless-stopped
env_file: .env
environment:
SMTP_HOST: ${SMTP_HOST}
SMTP_PORT: ${SMTP_PORT}
SMTP_TLS: ${SMTP_TLS}
SMTP_STARTTLS: ${SMTP_STARTTLS}
SMTP_TLS_CHECKCERT: "on"
SMTP_AUTH: "on"
SMTP_USER: ${SMTP_USER}
SMTP_PASSWORD: ${SMTP_PASSWORD}
SMTP_FROM: ${SMTP_FROM}
networks:
- homelab_net
networks:
homelab_net:
external: true
Warum kein ports:-Block?
Wer Artikel 03 gemacht hat, kennt das Prinzip schon: Container, die nur intern angesprochen werden, brauchen keine Host-Ports. Genau das ist beim Mail-Relay der Fall. Apps wie Uptime Kuma oder Authelia laufen im selben homelab_net und erreichen den Relay einfach über seinen Service-Namen: mail-relay:2500. Der Port verlässt den Host nie.
Würden wir Port 25 oder 2500 am Host veröffentlichen, hätten wir einen offenen SMTP-Eingang aus dem Internet – genau das Gegenteil dessen, was Artikel 03 und 07 mit der „Caddy als einziger Eingang"-Regel etabliert haben.
Was die einzelnen Blöcke machen
| Block | Erklärung |
|---|---|
name: mail-relay |
Expliziter Projekt-Name (Pattern aus Artikel 03) |
container_name: mail-relay |
Container heißt im docker ps und im Docker-DNS einheitlich mail-relay |
env_file: .env |
Komodo speichert die .env direkt neben dem Stack – Secrets bleiben dort |
SMTP_TLS_CHECKCERT: "on" |
Fest auf „on" – Zertifikatsprüfung soll immer an sein, keine Option |
homelab_net |
Das gemeinsame Netz aus Artikel 03, in dem alle Apps liegen |
7. .env – Provider-Konfiguration
Im .env-Tab. Hier zwei Varianten, je nach Provider und gewünschter TLS-Variante.
Variante A: Netcup, STARTTLS auf Port 587
SMTP_HOST=mail.deinwebhosting.netcup.net
SMTP_PORT=587
SMTP_TLS=off
SMTP_STARTTLS=on
SMTP_USER=noreply@deine-domain.de
SMTP_PASSWORD=dein-mailbox-passwort
SMTP_FROM=noreply@deine-domain.de
Variante B: Manitu, SSL/TLS auf Port 465
SMTP_HOST=mail.manitu.de
SMTP_PORT=465
SMTP_TLS=on
SMTP_STARTTLS=off
SMTP_USER=noreply@deine-domain.de
SMTP_PASSWORD=dein-mailbox-passwort
SMTP_FROM=noreply@deine-domain.de
TLS oder STARTTLS – was ist der Unterschied?
Bei
SMTP_TLS=on(Port 465) startet die Verbindung sofort verschlüsselt. BeiSMTP_STARTTLS=on(Port 587) beginnt sie unverschlüsselt und wird per Kommando in eine verschlüsselte Verbindung gehoben. Beide sind verschlüsselt – nimm die Variante, die dein Provider empfiehlt. Wichtig: Genau eine der beiden Optionen mussonsein, die andereoff.
Hinweis zur Zustellbarkeit
Wenn deine FROM-Adresse auf derselben Domain liegt, die du beim Provider hostest, sind SPF und DKIM dort bereits eingerichtet – Mails kommen normal an. Solltest du von einer Adresse senden wollen, deren Domain woanders gehostet ist, musst du SPF/DKIM für diese Domain selbst im DNS ergänzen. Die Provider-Doku führt durch die Einträge.
8. Update-Verhalten konfigurieren
Im Stack-Editor, bevor du deployst:
- Poll for Updates:
on - Auto Update:
off
Standard für alle Stacks in dieser Serie.
9. Deploy und Container-Log prüfen
Im Stack-Overview: Deploy. Komodo zieht das Image und startet den Container.
In Komodo den Container öffnen → Tab Logs. Du siehst eine Reihe von Init-Meldungen – das Image nutzt s6-overlay als Init-System, daher die [s6-init]- und [cont-init.d]-Zeilen. Das ist kein Fehler, sondern dokumentiert nur, wie das Image hochfährt. Die entscheidenden Zeilen:
[cont-init.d] 02-config.sh: executing...
Setting timezone to UTC...
Creating configuration...
[cont-init.d] 02-config.sh: exited 0.
...
[services.d] starting services
[services.d] done.
Was du daran ablesen kannst:
- „Creating configuration..." gefolgt von
exited 0.– msmtpd hat aus den Env-Variablen eine valide Konfiguration gebaut. Fehlende Variablen oder Tippfehler würden hier mit einem Non-Zero-Exit auffallen. [services.d] done.– der Listener läuft.
Damit ist der Container betriebsbereit. Ob die Verbindung zum Provider wirklich klappt, sieht man hier noch nicht – msmtpd baut die Verbindung erst beim ersten echten Mail-Versand auf. Genau das verifizieren wir gleich mit swaks.
Wenn der Container in einer Restart-Loop hängt (z. B. wegen falscher Env-Variablen), prüfe in dieser Reihenfolge:
- Stimmt
SMTP_HOSTexakt? Tippfehler sind hier die häufigste Ursache. - Ist
SMTP_TLS/SMTP_STARTTLSzur Port-Wahl passend? (465 → TLS, 587 → STARTTLS) - Stimmt
SMTP_USERundSMTP_PASSWORD? Bei manchen Providern ist der User die volle Mail-Adresse, bei anderen nur der Kürzel-Name.
10. Test-Versand mit swaks
swaks (Swiss Army Knife for SMTP) ist das Standard-Tool für SMTP-Tests. Es ist in den Debian-Repos verfügbar – kein Drittanbieter-Image, einfach lokal installieren:
sudo apt update
sudo apt install -y swaks
Da der Mail-Relay bewusst keinen Port am Host veröffentlicht, brauchen wir kurz seine interne Docker-Netz-IP. Die holen wir uns einmal aus docker inspect:
MAIL_RELAY_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mail-relay)
echo $MAIL_RELAY_IP
Erwartung: Eine IP-Adresse aus dem Docker-Bridge-Bereich, z. B. 172.18.0.5.
Dann der eigentliche Test:
swaks \
--to deine-test-adresse@example.com \
--from noreply@deine.domain.de \
--server $MAIL_RELAY_IP:2500 \
--header "Subject: Test vom Mail-Relay" \
--body "Wenn diese Mail ankommt, läuft der Relay."
Du siehst im Terminal das komplette SMTP-Protokoll. Die Schlüsselzeilen am Ende:
<- 250 Ok
-> QUIT
<- 221 Bye
Wenige Sekunden später sollte die Mail im Postfach der Test-Adresse ankommen. Falls sie im Spam-Ordner landet: Beim ersten Versand von einer neuen Adresse passiert das gelegentlich. Ab dem zweiten Mal ist sie meist im Posteingang.
Was tun, wenn es nicht funktioniert
Der eigentliche Diagnose-Ort ist das Container-Log von
mail-relay(Komodo → Container → Logs). Dort steht eine Zeile pro Mail-Versand mit allen Details. Häufige Fehlerbilder:
cannot connect to <host>, port <port>: Operation timed out– TCP-Connect kommt nicht durch. Bei Netcup fast immer: SCP-Firewall-Policy noch aktiv (siehe Schritt 4). Quick-Check vom Host:nc -zv <host> <port>.authentication failed–SMTP_USERoderSMTP_PASSWORDin der.envfalsch. Manche Provider erwarten die volle Mail-Adresse als User, andere nur den Kürzel.Sender address rejected/relaying denied– Die--from-Adresse muss zur authentifizierten Mailbox (SMTP_USER) passen, mindestens auf derselben Domain.exitcode=EX_OKmit250 Ok: queued as ...aber Mail kommt nicht an – Mail wurde vom Provider angenommen. Drei mögliche Folgeursachen: Spam-Ordner des Empfängers, Greylisting (5–15 Min warten), oder Bounce in derSMTP_USER-Mailbox (Webmail aufmachen, nachMAILER-DAEMONsuchen).
Warum nicht einfach
echo "Test" | …?Eine reine Textzeile ist keine RFC-konforme Mail (keine Header, kein Subject, kein Date). Manche Empfänger nehmen so etwas trotzdem an, viele werfen sie als Spam weg oder filtern sie still aus. swaks erzeugt eine vollständige, valide Mail – damit testest du genau das, was deine Apps später senden werden.
Warum die Container-IP statt des Container-Namens?
Container-Namen wie
mail-relaysind nur innerhalb des Docker-Netzes als DNS-Namen auflösbar. Vom Host aus geht das nicht – swaks läuft aber genau dort. Über die direkte IP umgehen wir die Namensauflösung; technisch funktioniert das identisch. Apps wie Authelia sprechen später dannmail-relay:2500an, weil sie im selben Netz laufen.
swaks danach lassen oder deinstallieren?
swaks ist ein Perl-Skript, ein paar KB groß, kein laufender Dienst, keine offenen Ports. Du kannst es einfach drauflassen – beim nächsten Provider-Wechsel oder bei Debug-Bedarf ist es sofort zur Hand. Wer aufräumen will:
sudo apt remove swaks.
11. Eine App an den Relay anschließen
Ab jetzt nutzt jede App, die Mail verschicken will, einfach mail-relay:2500 – ohne TLS, ohne Auth, weil die Verbindung das Docker-Netz nie verlässt.
Beispiel: Authelia (kommt in Artikel 09). In Authelias configuration.yml:
notifier:
smtp:
address: smtp://mail-relay:2500
sender: "Authelia <noreply@deine-domain.de>"
# Kein username, kein password – der Relay übernimmt die Auth nach außen
disable_require_tls: true
disable_starttls: true
disable_require_tls und disable_starttls sind hier kein Sicherheitsproblem: Die Verbindung läuft nur im Docker-Netz zwischen Authelia und dem Relay. Erst der Relay redet TLS-verschlüsselt mit Netcup/Manitu.
Anderes Beispiel: Uptime Kuma. Da konfigurierst du den SMTP-Versand über die UI:
| Feld | Wert |
|---|---|
| Hostname | mail-relay |
| Port | 2500 |
| Secure | aus |
| Authentifizierung | aus |
| From | noreply@deine-domain.de |
Dasselbe Muster gilt für jede App in dieser Serie: Hostname mail-relay, Port 2500, keine Auth, kein TLS – das macht alles der Relay.
12. Provider-Wechsel in unter einer Minute
Wenn du irgendwann von Netcup zu Manitu wechselst (oder umgekehrt), brauchst du genau eine Anpassung: die .env im mail-relay-Stack.
- In Komodo: Stack
mail-relay→.env-Tab - Provider-Werte austauschen (Variante A ↔ Variante B aus Schritt 6)
- Stack neu Deployen
- Test mit swaks (Schritt 8)
Keine einzige App-Compose wird angefasst. Genau dafür gibt es den Relay.
Was der Relay nicht kann (und warum das hier OK ist)
Ehrlichkeit zum Schluss: msmtpd ist bewusst minimalistisch.
- Keine Mail-Queue. Schlägt eine Zustellung fehl – etwa weil der Provider gerade nicht erreichbar ist – ist die Mail weg. Im Container-Log siehst du den Fehler. Bei den niedrigen Volumina dieser Serie (ein paar Notifications pro Tag) und der hohen Verfügbarkeit von Netcup/Manitu ist das in der Praxis kein Thema.
- Kein eingehender Mail-Empfang. Der Relay sendet nur, er empfängt nichts. Für Self-Hosting-Notifications reicht das.
- Keine Mehr-Provider-Logik. Ein Relay = ein Upstream. Wer Failover braucht, fährt mehrere Stacks (selten nötig).
Für alles darüber hinaus gibt es schwergewichtigere Alternativen, die in dieser Serie aber nicht gebraucht werden.
Anhang A: Direkt-Anbindung (Option 2)
Wenn du wirklich nur eine einzige App betreibst, die Mail will, und keinen Relay-Container für sinnvoll hältst, kannst du SMTP-Credentials direkt in der App-Compose hinterlegen.
Beispiel: Uptime Kuma direkt an Manitu:
In der App-Konfiguration (UI oder env-Vars):
SMTP_HOST=mail.manitu.de
SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=noreply@deine-domain.de
SMTP_PASSWORD=dein-passwort
Vorteil: Kein zusätzlicher Container.
Nachteil: Sobald eine zweite App dazukommt, hast du das Passwort an zwei Stellen. Bei drei Apps wird es unübersichtlich. Provider-Wechsel bedeutet: jede App einzeln anfassen.
Empfehlung: Wenn du sicher bist, dass es bei einer Mail-App bleibt, ist das OK. Sobald die zweite kommt, lohnt sich der Wechsel auf den Relay.
Anhang B: Ohne Mail (Option 1)
Manche Apps lassen sich komplett ohne Mail betreiben. Authelia z. B. funktioniert mit TOTP als 2FA – die Mail-Funktionen sind optional. Uptime Kuma kann statt Mail auch an ntfy, Discord, Slack oder Telegram pushen.
Wenn keine App in deinem Setup Mail braucht, gibt es nichts zu konfigurieren. Lass den Relay-Stack einfach weg.
Und jetzt?
Du hast einen sauberen, zentralen Mail-Versand. Alle weiteren Apps dieser Serie hängen sich mit mail-relay:2500 ein – ohne TLS, ohne Auth, ohne Provider-Wissen. Wechselst du den Provider, ist das eine Sache von einer Minute und einer einzigen Datei.
Im nächsten Artikel kommt Authelia – die Authentifizierungs-Schicht mit 2FA, die vor allen persönlichen Apps sitzen wird (Vaultwarden, Immich, Paperless). Authelia ist auch der erste Baustein, der den frisch eingerichteten Mail-Relay tatsächlich nutzen kann – für Passwort-Reset-Mails und Setup-Links.
👉 Nächster Artikel: 09. Authelia – SSO mit 2FA für persönliche Apps
Discussion