Skip to content

Email: Inbound (MTA-in)

Incoming email flows through the MTA-in Postfix container, which delivers messages to the Django backend via a custom milter.

Flow

External SMTP server
       ▼ (port 25)
   MTA-in (Postfix)
       ▼ (milter)
   Django MDA (backend:8000)
   Messages inbox (PostgreSQL)

Requirements

  1. MX record pointing to mx.<domain>
  2. Port 25 open inbound (firewall + VPS provider)
  3. MTA-in container running as root (milter socket permissions)

Milter Permission Fix

Without user: root, the milter socket at /var/spool/postfix/milter/delivery.sock has incorrect permissions, causing:

warning: connect to Milter service unix:/var/spool/postfix/milter/delivery.sock: Permission denied
451 4.7.1 Service unavailable - try again later

The fix in compose.yml:

mta-in:
  user: root   # Required for milter socket permissions

Verification

Send a test email:

python3 -c "
import smtplib, email.mime.text
msg = email.mime.text.MIMEText('Test inbound')
msg['From'] = 'test@example.com'
msg['To'] = '<user>@<domain>'
msg['Subject'] = 'Test inbound delivery'
s = smtplib.SMTP('localhost', 25, timeout=10)
s.sendmail('test@example.com', ['<user>@<domain>'], msg.as_string())
s.quit()
print('Sent')
"

Then check the backend logs:

docker compose logs backend | grep "inbound\|Raw email\|Queued"

Firewall

  • Hetzner Cloud: Add incoming TCP/25 rule explicitly (not open by default)
  • Other providers: Check if port 25 is blocked by default