Der direkte Weg aus PHP heraus über einen externen SMTP-Server (wie Mailgun, SendGrid oder den Office365/Google Workspace-Account des Kunden) ist zwar schnell eingerichtet, birgt im Produktivbetrieb jedoch massive Nachteile. Diese Services können manchmal nicht erreichbar sein und sie sind oft (sehr sehr) langsam. Wird jetzt innerhalb eines Workflows (Login, Bestellung im Shop, Formular versenden, Forenbeitrag erstellen) synchron auf den Mailversand gewartet, kann das für die Besucher zu langen Wartezeiten, Frustration und dadurch zum Abbruch der Aktion führen.
Die Lösung ist ein schlanker, containerisierter Postfix-MTA (Mail Transfer Agent), der als lokaler Relay fungiert.
In diesem Beitrag betrachten wir die Vor- und Nachteile dieses Setups und zeigen, wie die Integration via docker-compose.yml und TYPO3 nahtlos gelingt.
Das Problem mit synchronem SMTP in PHP
PHP-Anwendungen arbeiten standardmäßig synchron. Wenn ein Benutzer im Frontend ein Kontaktformular absendet und TYPO3 so konfiguriert ist, dass es sich direkt per SMTP mit einem externen Provider verbindet, passiert Folgendes:
- PHP öffnet eine Socket-Verbindung zum externen SMTP-Server.
- Der TLS-Handshake wird durchgeführt.
- Die Authentifizierungsdaten werden ausgetauscht.
- Die Mail-Daten werden übertragen.
- Der externe Server quittiert den Empfang.
Dieser gesamte Prozess kann problemlos 1 bis 3 Sekunden dauern. Wir haben Kundenserver, die bis zu 20 Sekunden für die Bestätigung des erfolgreichen Mailversandes benötigen Für den Benutzer bedeutet das: Das Rädchen dreht sich, die Seite blockiert. Schlägt der externe SMTP-Server fehl (Timeout oder Rate-Limiting), bricht der PHP-Prozess im schlimmsten Fall mit einem Error ab, und die Mail ist unwiederbringlich verloren.
Die Lösung: Nebenläufigkeit durch einen lokalen Postfix-Container
Indem wir einen leichtgewichtigen Postfix-Container (wie das exzellente Image von boky/postfix) in unseren Docker-Stack integrieren, entkoppeln wir den Mailversand komplett von der PHP-Ausführung.
Vorteile des Setups
- Echte Nebenläufigkeit (Asynchroner Versand): TYPO3 übergibt die Mail in Millisekunden an den lokalen Postfix-Container auf Port 25. Für PHP ist der Job damit erledigt, das Frontend reagiert sofort. Postfix kümmert sich im Hintergrund (asynchron) um die Weiterleitung an den eigentlichen SMTP-Provider.
- Robuste Warteschlange (Spooling / Queuing): Ist der externe SMTP-Relay temporär nicht erreichbar oder greift ein Rate-Limit, hält Postfix die Mails in der eigenen Queue und versucht den Versand periodisch erneut. Es gehen keine Mails verloren.
- Zentralisierte Authentifizierung: Die sensiblen SMTP-Zugangsdaten des Providers liegen nur an einer einzigen Stelle (im Postfix-Container). Die PHP-Anwendung selbst benötigt keine Passwörter, sondern vertraut dem lokalen Netzwerk.
- Sicherheits-Eingrenzung: Über Direktiven wie ALLOWED_SENDER_DOMAINS lässt sich granular steuern, welche Absender-Domains der Container überhaupt akzeptieren darf.
Nachteile & Herausforderungen
- Zusätzlicher Container-Overhead: Ein weiterer Service, der im Docker-Stack überwacht, geloggt und gewartet werden muss. Wobei wir hier einen Speicherverbrauch im unteren zweistelligen MB-Bereich beobachten. Das aktuelle Beispiel verursacht einen Overhead von 25MB im RAM des Servers.
- Sicherheitsrisiko bei Fehlkonfiguration: Wenn POSTFIX_mynetworks zu offen definiert ist, baut man sich im schlimmsten Fall ein Open Relay, das von Angreifern für Spam missbraucht werden kann. Es muss strikt auf das interne Docker-Netzwerk isoliert werden.
- Flüchtige Queue bei Container-Restarts: Liegen Mails in der Postfix-Queue und der Container wird hart gestoppt/gelöscht (ohne persistent verknüpftes Volume für /var/spool/postfix), sind diese Mails verloren.
Die technische Integration
1. Docker-Compose Konfiguration
Hier ist das ein beispielhaftes Snippet für die docker-compose.yml. Wir nutzen das boky/postfix Image, das sich hervorragend über Umgebungsvariablen konfigurieren lässt:
services:
# ... TYPO3 / PHP Services, Datenbank, Cron, ...
postfix:
container_name: mysite_postfix
image: boky/postfix:latest
hostname: relay
logging:
options:
max-size: "2m"
max-file: "10"
environment:
- RELAYHOST=${SMTP_HOST}
- RELAYHOST_USERNAME=${SMTP_USER}
- RELAYHOST_PASSWORD=${SMTP_PASS}
- POSTFIX_mynetworks=127.0.0.0/8,10.10.0.0/16
- ALLOWED_SENDER_DOMAINS=${ALLOWED_SENDER_DOMAINS}
- POSTFIX_smtpd_tls_security_level=none
- POSTFIX_smtp_tls_security_level=may
In der zugehörigen .env Datei stehen dann die notwendigen Daten
SMTP_HOST=[smtpserver.mydomain.de]:587
SMTP_USER=smtpusername
SMTP_PASS=sicheressmtppassword
ALLOWED_SENDER_DOMAINS=mydomain.de
Wichtige Details im Fokus:
- POSTFIX_mynetworks: Erlaubt Verbindungen von localhost und dem internen Docker-Subnetz (hier beispielhaft 10.10.0.0/16 – dies sollte an das tatsächliche Docker-Netzwerk angepasst werden).
- POSTFIX_smtpd_tls_security_level=none: Da die Kommunikation zwischen dem PHP-Container und Postfix innerhalb des internen, isolierten Docker-Netzwerks stattfindet, ist hier keine Verschlüsselung notwendig.
- POSTFIX_smtp_tls_security_level=may: Aktiviert TLS für die ausgehende Verbindung von Postfix zum externen Provider (RELAYHOST).
2. Konfiguration in TYPO3
Da Postfix die Authentifizierung gegenüber dem finalen Provider übernimmt, schrumpft die Mail-Konfiguration im TYPO3-Install-Tool (bzw. in der LocalConfiguration.php / AdditionalConfiguration.php) auf ein Minimum. TYPO3 sendet unverschlüsselt und ohne Login an den Postfix-Container:
return [
'MAIL' => [
'defaultMailFromAddress' => 'admin@mydomain.de',
'defaultMailFromName' => 'RMS Admin',
'defaultMailReplyToAddress' => 'admin@mydomain.de',
'defaultMailReplyToName' => 'admin@mydomain.de',
'transport' => 'smtp',
'transport_smtp_domain' => 'mydomain.de',
'transport_smtp_encrypt' => false,
'transport_smtp_password' => '',
'transport_smtp_server' => 'postfix:25',
'transport_smtp_username' => '',
],
];
Fazit
Die Auslagerung des Mailversands in einen dedizierten Postfix-Relay-Container ist ein Best-Practice-Pattern für den produktiven Betrieb von TYPO3 und anspruchsvollen PHP-Anwendungen. Es eliminiert die gefürchteten Ladezeit-Spikes beim Absenden von Formularen und schützt dank der integrierten Queue vor Datenverlust bei Ausfällen von Drittanbieter-Diensten. Der minimale Mehraufwand bei der Docker-Konfiguration zahlt sich durch eine stabilere Architektur und eine spürbar bessere User Experience sofort aus.