postfix

Installation
SKILL.md

Identity

  • Unit: postfix.service (systemd); master process is /usr/lib/postfix/sbin/master
  • Config: /etc/postfix/main.cf (main parameters), /etc/postfix/master.cf (daemon table)
  • Queue dirs: /var/spool/postfix/ — subdirs: active, deferred, hold, incoming, corrupt
  • Logs: journalctl -u postfix, /var/log/mail.log (Debian/Ubuntu), /var/log/maillog (RHEL/Fedora)
  • Ports: 25/tcp (SMTP), 587/tcp (submission/MSA), 465/tcp (SMTPS legacy)
  • Distro install: apt install postfix / dnf install postfix
  • Lookup table rebuild: postmap /etc/postfix/<table> — required after editing hash-type maps

Key Operations

Operation Command
Service status systemctl status postfix
Start / stop / restart systemctl start|stop|restart postfix
Reload config (no restart) sudo postfix reload
Test config syntax sudo postfix check
Show all active parameters postconf -n (non-default only) or postconf (all)
Show single parameter value postconf myhostname
Send test email echo "Test body" | mail -s "Test subject" user@example.com
Send test with sendmail syntax echo -e "Subject: Test\n\nBody" | sendmail -v recipient@example.com
Check queue (all messages) mailq or postqueue -p
Queue message count postqueue -p | tail -1
Flush deferred queue now sudo postqueue -f
Flush queue for one domain sudo postqueue -s example.com
View message content sudo postcat -q <queue-id>
Remove one message from queue sudo postsuper -d <queue-id>
Remove all deferred messages sudo postsuper -d ALL deferred
Hold a message sudo postsuper -h <queue-id>
Release held message sudo postsuper -H <queue-id>
Re-queue held messages sudo postsuper -H ALL hold
Trace message routing sudo postmap -q user@example.com virtual
Test SMTP connection locally telnet localhost 25 then EHLO test
Check if port 25 is reachable nc -zv mail.example.com 25
Check outbound port 25 blocked nc -zv smtp.gmail.com 25 (timeout = ISP/cloud block)
View active SMTP connections ss -tnp | grep :25
Test postmap hash lookup postmap -q "key" hash:/etc/postfix/virtual
View mail log live journalctl -u postfix -f or tail -f /var/log/mail.log

Expected Ports

  • 25/tcp (SMTP): Server-to-server mail transfer; often blocked outbound by ISPs and cloud providers
  • 587/tcp (Submission/MSA): Authenticated client submission; use this for sending from mail clients
  • 465/tcp (SMTPS): Legacy implicit TLS submission; still used by some clients

Verify listening: ss -tlnp | grep master Firewall (ufw): sudo ufw allow 25,587/tcp Firewall (firewalld): sudo firewall-cmd --add-service=smtp --permanent && sudo firewall-cmd --reload

Health Checks

  1. systemctl is-active postfixactive
  2. sudo postfix check 2>&1 → no output means no errors
  3. postconf myhostname mydomain myorigin → values match your actual hostname and domain
  4. echo "test" | sendmail -v root → check journalctl -u postfix -n 20 for delivery attempt

Common Failures

Symptom Likely cause Check / Fix
Connection refused on port 25 from outside Cloud/ISP blocks outbound port 25 nc -zv smtp.gmail.com 25; use SMTP relay (SendGrid, SES, Mailjet) on port 587 instead
Relay access denied inet_interfaces or mynetworks too restrictive postconf inet_interfaces mynetworks; add sending host to mynetworks or use SASL auth
Unknown user in local recipient table Missing alias or local_recipient_maps too strict Add to /etc/aliases then newaliases; or set local_recipient_maps = (empty) to disable check
Mail rejected by recipient (DNSBL) Server IP on a spam blocklist Check https://mxtoolbox.com/blacklists.aspx; request delisting or switch to relay service
Queue backing up, deferred messages growing DNS resolution failure, destination unreachable, or rate limit postqueue -p to see reason; `journalctl -u postfix
TLS handshake failed Certificate expired, wrong path, or protocol mismatch postconf smtpd_tls_cert_file; verify cert with openssl x509 -in /path/to/cert.pem -noout -dates
fatal: open /etc/postfix/main.cf: No such file or directory Postfix not installed or config missing apt install postfix / dnf install postfix; run postfix check
Mail delivered but lands in spam Missing or failing SPF/DKIM/DMARC Check headers of received mail; set up SPF DNS record, install opendkim, publish DMARC policy
Reverse DNS mismatch causing rejection PTR record doesn't match myhostname dig -x <your-ip>; update PTR with hosting provider to match myhostname in main.cf
Postfix is already running on start Stale PID file rm /var/spool/postfix/pid/master.pid; systemctl start postfix

Pain Points

  • Most cloud providers block outbound port 25 — AWS, GCP, Azure, DigitalOcean all restrict it by default. Configure Postfix as a null client relaying through an SMTP provider (SendGrid, SES, Mailjet) using port 587 with SASL. This is the standard homelab/cloud setup.
  • Deliverability requires SPF, DKIM, and DMARC — running your own MTA without these guarantees spam folder delivery. SPF is a DNS TXT record, DKIM requires opendkim, DMARC ties them together. None of this is automatic.
  • postmap must be run after editing lookup tables — changes to any hash: map file (virtual, transport, access, etc.) have no effect until you run postmap /etc/postfix/<filename>. Common source of "why isn't my alias working" confusion.
  • main.cf changes need postfix reload — editing the file is not enough. postfix reload applies changes without dropping connections; postfix check validates before reloading.
  • Reverse DNS (PTR record) should match myhostname — many receiving servers check that the connecting IP's PTR record matches the EHLO hostname. Mismatch triggers spam scoring or outright rejection. Set your PTR at the hosting provider level.
  • Greylisting at receiving servers delays first-time deliveries — a new server sending to a well-protected domain may wait 5–30 minutes for the first message to be accepted. This is normal behavior, not a Postfix problem. Subsequent messages go through immediately.

References

See references/ for:

  • main.cf.annotated — complete main.cf with every directive explained
  • common-patterns.md — relay setup, null client, SASL, virtual domains, TLS, queue management
  • docs.md — official documentation links
Related skills
Installs
1
GitHub Stars
5
First Seen
Mar 18, 2026