
Infrastructure
Postfix to FastAPI: Receive Emails and Pipe Them to an API Endpoint (Dummy Config)
Editor | February 28, 2026 | 6 min read
This guide shows how to receive emails on your server using Postfix and forward each incoming message to a FastAPI endpoint.
The flow is:
- Postfix accepts incoming mail for your domain.
- Postfix transport routes the message to a custom pipe.
- A Python script reads raw RFC822 email from stdin.
- The script parses
To,From,Subject, and body. - The script posts the parsed payload to FastAPI.
1. Update /etc/postfix/main.cf
Open file:
sudo nano /etc/postfix/main.cf
Use/update these settings:
smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no
append_dot_mydomain = no
readme_directory = no
compatibility_level = 3.6
# TLS (default/self-signed for now)
smtpd_tls_cert_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
smtpd_tls_security_level = may
smtp_tls_CApath = /etc/ssl/certs
smtp_tls_security_level = may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# Server identity (dummy values)
myhostname = mail.example.test
mydomain = example.test
myorigin = $mydomain
# Network
inet_interfaces = all
inet_protocols = ipv4
# Virtual domain
mydestination = localhost
virtual_mailbox_domains = example.test
transport_maps = hash:/etc/postfix/transport
# Security (prevent open relay)
smtpd_recipient_restrictions =
permit_mynetworks,
reject_unauth_destination
# Basic settings
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
Reload Postfix:
sudo systemctl reload postfix
2. Add Pipe Service in /etc/postfix/master.cf
Open file:
sudo nano /etc/postfix/master.cf
Add this at the end:
mailpipe unix - n n - - pipe
flags=Rq user=appuser argv=/opt/mailpipe/mail_pipe.py
Reload Postfix:
sudo systemctl reload postfix
3. Add Transport Mapping
Open and edit:
sudo nano /etc/postfix/transport
Add:
example.test mailpipe:
Build transport db:
sudo postmap /etc/postfix/transport
4. Create mail_pipe.py
Create script:
nano /opt/mailpipe/mail_pipe.py
chmod +x /opt/mailpipe/mail_pipe.py
Use this script:
#!/usr/bin/env python3
import sys
import requests
from email import message_from_bytes
from email.policy import default
from datetime import datetime
DEBUG_LOG = "/var/log/mailpipe/debug.log"
ERROR_LOG = "/var/log/mailpipe/error.log"
FASTAPI_URL = "http://127.0.0.1:8000/api/v1/inbound-email"
def log_debug(message: str):
timestamp = datetime.utcnow().isoformat()
with open(DEBUG_LOG, "a", encoding="utf-8") as f:
f.write(f"[{timestamp}] {message}\\n")
def log_error(message: str):
timestamp = datetime.utcnow().isoformat()
with open(ERROR_LOG, "a", encoding="utf-8") as f:
f.write(f"[{timestamp}] {message}\\n")
def extract_body(msg):
if msg.is_multipart():
for part in msg.walk():
if part.get_content_type() == "text/plain":
return part.get_content()
return ""
return msg.get_content()
def main():
try:
raw_email = sys.stdin.buffer.read()
log_debug(f"Pipe executed. Email size: {len(raw_email)} bytes")
msg = message_from_bytes(raw_email, policy=default)
to_addr = msg.get("To")
from_addr = msg.get("From")
subject = msg.get("Subject")
body = extract_body(msg)
log_debug(f"Parsed Email - To: {to_addr}, From: {from_addr}, Subject: {subject}")
log_debug(f"Body Preview (first 200 chars): {body[:200]}")
payload = {
"to": to_addr,
"sender": from_addr,
"subject": subject,
"body": body,
}
response = requests.post(
FASTAPI_URL,
json=payload,
timeout=10,
)
if response.status_code >= 400:
raise Exception(f"FastAPI error: {response.status_code} {response.text}")
log_debug("Email successfully sent to FastAPI")
except Exception as e:
log_error(str(e))
if __name__ == "__main__":
main()
Restart Postfix:
sudo systemctl restart postfix
5. Quick Test
Send a test email to any address on @example.test and verify:
/var/log/mailpipe/debug.logfor successful parse/send logs/var/log/mailpipe/error.logfor failures- FastAPI endpoint receives payload:
{
"to": "user@example.test",
"sender": "sender@example.com",
"subject": "Test",
"body": "Hello from SMTP"
}
Troubleshooting Notes
- If mail is not routed to script, re-check
/etc/postfix/transportand runsudo postmap /etc/postfix/transportagain. - If script does not run, verify execute permission and exact path in
master.cf. - If API calls fail, verify FastAPI service is reachable at
127.0.0.1:8000. - If endpoint needs auth, add headers/token in
requests.post(...).
Security Hardening (Recommended)
- Replace snakeoil certs with valid TLS certificates.
- Validate/allowlist recipient domains in FastAPI.
- Add rate limits and queue/retry behavior for endpoint failures.
- Rotate and protect log files.
This setup is a practical starting point for building inbound email automation with Postfix and FastAPI.