Ich wache also morgens auf, trinke gemütlich meinen Kaffee, öffne mein Mail-Interface – und… Stille. Kein Mailversand, kein Empfang.
Das Login-Interface meiner Mailcow-Instanz (auf einem Hetzner ARM64-Server) lud zwar noch, aber die Mails steckten fest. Der Server konnte nicht einmal mehr eine IP curlen.
Ich ahnte schon: Das war wohl das Debian-Update von letzter Nacht. 🙈
🐍 Der erste Verdächtige: Unbound
Ein Blick ins docker compose ps offenbarte:
mailcowdockerized-unbound-mailcow-1 ... unhealthy
Und das Log zeigte den Übeltäter:
curl: (28) Failed to connect to www.internic.net port 443 ...
Unbound wollte beim Start die Root-Hints von internic.net ziehen – doch ohne funktionierendes DNS (ja, Henne-Ei-Problem) blieb er stecken.
Das Resultat:
Mailcow startete zwar, aber alle Container, die auf DNS angewiesen sind – von Postfix bis Rspamd – liefen ins Leere.
⚙️ Schritt 1 – Docker wieder Internet geben
Das Debian-Update hatte offenbar das Netzwerk-Setup oder systemd-resolved durcheinandergebracht.
Lösung: Docker bekommt feste Resolver.
cat >/etc/docker/daemon.json <<'JSON'
{
"dns": ["1.1.1.1", "9.9.9.9"]
}
JSON
systemctl restart docker
Damit hat jeder Container beim Start Zugriff auf Cloudflare und Quad9.
⚡️ Schritt 2 – Root-Hints manuell nachladen
Da Unbound sich die Root-Zone-Datei nicht mehr ziehen konnte, hab ich sie selbst bereitgestellt:
curl -fsSL https://www.internic.net/domain/named.root -o /tmp/root.hints
docker cp /tmp/root.hints mailcowdockerized-unbound-mailcow-1:/etc/unbound/root.hints
docker exec -it mailcowdockerized-unbound-mailcow-1 sh -c \
'unbound-anchor -a /var/lib/unbound/root.key -v; chown unbound:unbound /var/lib/unbound/root.key'
docker compose restart unbound-mailcow
Nach einem Neustart war der Container endlich wieder healthy. 🎉
📬 Schritt 3 – „Temporary lookup failure“ beim Mailversand
Doch kaum dachte ich, das System sei wieder heil, kam der nächste Schlag:
451 4.3.0 Temporary lookup failure
warning: texthash:/opt/mailcow-dockerized/data/conf/postfix/transport-extra.cf ...
Postfix beschwerte sich über eine fehlende Transport-Map.
Diese transport-extra.cf wird standardmäßig referenziert, existierte aber schlicht nicht mehr.
🔧 Schritt 4 – Postfix in Docker richtig konfigurieren
Das Problem: In den Konfig-Dateien stand ein Host-Pfad (/opt/mailcow-dockerized/...), den der Container gar nicht kennt.
Im Container-Kontext muss die Variable ${config_directory} verwendet werden – sie zeigt auf /etc/postfix.
Ein beherztes Suchen & Ersetzen brachte die Rettung:
grep -Rin "transport-extra\.cf|transport_maps" /opt/mailcow-dockerized/data/conf/postfix
sed -i 's#/opt/mailcow-dockerized/data/conf/postfix/transport-extra\.cf#${config_directory}/transport-extra.cf#g' \
/opt/mailcow-dockerized/data/conf/postfix/*.cf
Und natürlich eine leere Datei angelegt, falls sie nicht existiert:
touch /opt/mailcow-dockerized/data/conf/postfix/transport-extra.cf
chmod 644 /opt/mailcow-dockerized/data/conf/postfix/transport-extra.cf
docker compose restart postfix-mailcow
Ab da: keine Lookup-Fehler mehr. 🥳
✅ Fazit – Fehlerursache & Lessons Learned
Der ganze Crash kam durch eine Kettenreaktion:
- Debian-Update →
systemd-resolved/ Docker-DNS gebrochen - Unbound konnte keine Root-Hints laden → „unhealthy“
- Postfix bekam keine DNS-Antworten → Lookup-Fehler
- Zusätzlich: Falscher Pfad zu
transport-extra.cf→ 451-Fehler bei RCPT
Dauerhafte Lösung:
- Docker mit festen Resolvern (
1.1.1.1,9.9.9.9) - Unbound-Root-Hints und Trust-Anchor manuell initialisiert
- Postfix-Maps auf
${config_directory}umgestellt - Leere
transport-extra.cfbereitgestellt
Seitdem läuft der Mailcow-Stack wieder sauber – und ich trinke meinen Kaffee wieder entspannt. ☕️
🧠 Bonus-Tipp:
Nach größeren System-Updates lohnt sich ein schneller Testlauf:
docker inspect -f '{{.State.Health.Status}}' mailcowdockerized-unbound-mailcow-1
docker exec -it mailcowdockerized-postfix-mailcow-1 postconf -n | grep transport_maps
So merkt man sofort, ob Unbound oder Postfix zickt – bevor das erste „Warum bekomme ich keine Mails mehr?“ eintrifft. 😉


