Postfix is the most widely deployed open-source mail transfer agent — it is the default MTA on most Linux distributions and the sending backbone for many small-to-medium commercial email programmes. For programmes below 500,000 monthly messages that require a self-managed MTA without the licensing cost of PowerMTA, Postfix configured correctly provides reliable, authenticated delivery with good deliverability characteristics. This guide covers the Postfix configuration specific to commercial outbound email: DKIM signing, TLS setup, rate limiting, and the monitoring configuration that provides delivery visibility without PowerMTA's accounting log.
Postfix Overview and When to Use It
Postfix is appropriate for commercial email programmes that need a self-managed MTA with full authentication configuration and direct IP control, at volumes below approximately 500,000 messages per month. Its strengths: free and open source (no licensing cost), extremely stable and well-documented, integrates cleanly with standard Linux mail tools (OpenDKIM, Amavis, SpamAssassin), and provides direct access to mail logs for delivery monitoring. Its limitations compared to PowerMTA: no built-in per-domain connection pool management with the granularity of PowerMTA's domain blocks, no native accounting log in structured format (delivery monitoring requires log parsing), and less flexible queue management for high-volume batch sending. For programmes below the volume threshold where PowerMTA's additional capabilities are needed, Postfix is the correct choice — it is operationally simpler and costs nothing.
Installation and Basic Configuration
Install Postfix on AlmaLinux 8/RHEL 8 or Debian/Ubuntu:
# AlmaLinux 8 / RHEL 8 dnf install postfix -y systemctl enable postfix systemctl start postfix # Ubuntu/Debian apt-get install postfix -y systemctl enable postfix
The primary Postfix configuration file is /etc/postfix/main.cf. The minimum configuration for outbound commercial email sending:
# /etc/postfix/main.cf — basic outbound configuration # Server identity myhostname = mail1.brand.com # Must match PTR record mydomain = brand.com myorigin = $mydomain # Accept mail only from localhost (relay server configuration) inet_interfaces = loopback-only # Only accept from local applications mynetworks = 127.0.0.0/8 # Don't accept incoming mail for local delivery (outbound relay only) mydestination = # Relay host (empty = deliver directly to destination MX) relayhost = # Queue management maximal_queue_lifetime = 5d # Maximum time a message waits bounce_queue_lifetime = 1d # Fast bounce for permanent failures minimal_backoff_time = 300s # Minimum retry wait (5 minutes) maximal_backoff_time = 4000s # Maximum retry wait (~66 minutes) # SMTP client settings (outbound) smtp_connect_timeout = 30s smtp_helo_timeout = 30s smtp_rcpt_timeout = 30s smtp_data_init_timeout = 120s smtp_data_done_timeout = 600s # Hostname in EHLO/HELO must match PTR record smtp_helo_name = $myhostname # Connection limits default_destination_concurrency_limit = 20 # Max concurrent connections per domain smtp_destination_concurrency_limit = $default_destination_concurrency_limit smtp_destination_rate_delay = 0 # No delay between messages per connection # Allow slightly more connections to major ISPs gmail.com_destination_concurrency_limit = 20 outlook.com_destination_concurrency_limit = 15 yahoo.com_destination_concurrency_limit = 15
DKIM Signing with OpenDKIM
OpenDKIM is the standard DKIM signing milter for Postfix. Installation and configuration for outbound DKIM signing:
# Install OpenDKIM dnf install opendkim opendkim-tools -y # Generate 2048-bit DKIM key pair for brand.com mkdir -p /etc/opendkim/keys/brand.com opendkim-genkey -b 2048 -d brand.com -s mail -D /etc/opendkim/keys/brand.com/ chown -R opendkim:opendkim /etc/opendkim/keys/ # View the DNS record to publish: cat /etc/opendkim/keys/brand.com/mail.txt # Example output: # mail._domainkey IN TXT "v=DKIM1; k=rsa; p=PUBLIC_KEY_HERE" # Publish this TXT record in brand.com DNS
Configure OpenDKIM (/etc/opendkim.conf):
# /etc/opendkim.conf Mode sv # Sign and verify (sv) or sign only (s) PidFile /run/opendkim/opendkim.pid Syslog yes SyslogSuccess yes LogWhy yes # Signing configuration Domain brand.com KeyFile /etc/opendkim/keys/brand.com/mail.private Selector mail SignatureAlgorithm rsa-sha256 Canonicalization relaxed/relaxed # Headers to sign (cover the headers that matter for deliverability) SignHeaders from,to,subject,date,message-id,content-type,mime-version, list-unsubscribe,list-unsubscribe-post # Socket for Postfix milter integration Socket local:/run/opendkim/opendkim.sock
Connect OpenDKIM to Postfix by adding to /etc/postfix/main.cf:
# /etc/postfix/main.cf — add milter configuration smtpd_milters = local:/run/opendkim/opendkim.sock non_smtpd_milters = local:/run/opendkim/opendkim.sock milter_default_action = accept # If milter unavailable, still deliver milter_protocol = 6
TLS Configuration for Outbound Sending
TLS is required for MAGY compliance — all outbound SMTP connections must use TLS encryption. Configure Postfix's SMTP client (outbound sender) for TLS:
# /etc/postfix/main.cf — TLS outbound configuration # Require TLS for all outbound connections smtp_tls_security_level = encrypt # encrypt = require TLS, fail if unavailable smtp_tls_mandatory_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1 smtp_tls_mandatory_ciphers = high # Require strong ciphers smtp_tls_loglevel = 1 # Log TLS connection details smtp_tls_CAfile = /etc/pki/tls/certs/ca-bundle.crt # CA certificate bundle # Session caching for performance smtp_tls_session_cache_database = btree:/var/lib/postfix/smtp_scache smtp_tls_session_cache_timeout = 3600s # For the sending hostname's TLS certificate (required for inbound, used for EHLO) smtpd_tls_cert_file = /etc/letsencrypt/live/mail1.brand.com/fullchain.pem smtpd_tls_key_file = /etc/letsencrypt/live/mail1.brand.com/privkey.pem
Obtain a TLS certificate for the sending hostname using Let's Encrypt (free) or a commercial CA. The certificate must be valid for mail1.brand.com (the hostname configured in myhostname). Renew Let's Encrypt certificates every 90 days — set up automated renewal with certbot:
# Install certbot and obtain certificate dnf install certbot -y certbot certonly --standalone -d mail1.brand.com # Auto-renewal cron (runs twice daily, renews within 30 days of expiry) echo "0 0,12 * * * root certbot renew --quiet --post-hook 'postfix reload'" > /etc/cron.d/certbot-renewal
Rate Limiting and Per-Domain Controls
Postfix supports per-domain sending rate controls through the smtp_destination_concurrency_limit and per-domain override configuration. For commercial email, calibrate these per major ISP:
# /etc/postfix/main.cf — per-domain rate controls # Global defaults default_destination_concurrency_limit = 5 # Conservative default for unknown domains smtp_destination_concurrency_limit = $default_destination_concurrency_limit # Override per major ISP (add as transport_maps entries or direct overrides) # Major ISP rate limits at High reputation: gmail.com_destination_concurrency_limit = 20 yahoo.com_destination_concurrency_limit = 15 ymail.com_destination_concurrency_limit = 15 outlook.com_destination_concurrency_limit = 12 hotmail.com_destination_concurrency_limit = 12 live.com_destination_concurrency_limit = 12 msn.com_destination_concurrency_limit = 12 # EU ISPs at moderate volume gmx.com_destination_concurrency_limit = 10 gmx.net_destination_concurrency_limit = 10 web.de_destination_concurrency_limit = 10 # Message rate limits (messages per connection) smtp_destination_recipient_limit = 100 # Max recipients per connection
SPF and Sender Restriction Configuration
For an outbound relay configuration (where Postfix only sends email from authorised applications), configure sender restrictions to prevent unauthorized use:
# /etc/postfix/main.cf — sender restrictions for relay security
# These apply to SMTP clients submitting email through Postfix
smtpd_sender_restrictions =
permit_mynetworks, # Allow from local network (authorised applications)
reject_unknown_sender_domain # Reject if sender domain has no MX or A record
smtpd_recipient_restrictions =
permit_mynetworks,
reject_unauth_destination # Prevent open relay
# Submission port (587) for authenticated client connections
# /etc/postfix/master.cf — add submission service
# submission inet n - n - - smtpd
# -o smtpd_tls_security_level=encrypt
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_sasl_type=dovecot
Postfix Logging and Monitoring
Postfix logs all delivery events to the system mail log (/var/log/maillog on AlmaLinux, /var/log/mail.log on Debian/Ubuntu). The log format is human-readable but requires parsing for structured monitoring. Each delivery event produces a log line with the queue ID, the recipient, the destination server, the SMTP response, and the delivery status:
# Example Postfix delivery log entries: Nov 19 14:23:01 mail1 postfix/smtp[12345]: 3A4B5C6D7E8F: to=<recipient@gmail.com>, relay=gmail-smtp-in.l.google.com[142.250.31.26]:25, delay=0.8, delays=0.05/0.01/0.5/0.24, dsn=2.0.0, status=sent (250 2.0.0 OK) # Bounce event: Nov 19 14:24:15 mail1 postfix/smtp[12346]: 3A4B5C6D7EFF: to=<invalid@example.com>, relay=mx.example.com[198.51.100.1]:25, delay=1.2, dsn=5.1.1, status=bounced (550 5.1.1 User unknown) # Throttle (4xx) event: Nov 19 14:25:32 mail1 postfix/smtp[12347]: 3A4B5C6D7FAA: to=<user@yahoo.com>, relay=mta7.am0.yahoodns.net[98.137.65.151]:25, delay=4.3, dsn=4.7.0, status=deferred (421 4.7.0 [TSS04] Messages deferred)
For structured monitoring from Postfix logs, use pflogsumm (Postfix Log Summary) for daily reports or a log parsing script that extracts delivery status, SMTP response codes, and destination domains into a structured database for operational queries. The monitoring gap versus PowerMTA: Postfix does not provide the per-event structured data that PowerMTA's accounting log delivers — monitoring requires post-processing of human-readable log files rather than real-time consumption of structured per-event records.
Postfix vs PowerMTA: When to Upgrade
The indicators that suggest upgrading from Postfix to PowerMTA (or a similar commercial MTA):
- Volume above 500K monthly: PowerMTA's connection pool management, per-domain domain blocks, and accounting log become operationally valuable at this volume.
- Multi-client / multi-tenant sending: PowerMTA's VMTA architecture allows per-client IP isolation that Postfix cannot replicate without complex separate Postfix instances.
- Deliverability investigation time: If more than 2 hours per month is spent diagnosing delivery problems from Postfix log parsing that would be resolved in minutes with PowerMTA's structured accounting log data, the operational time cost justifies the licence investment.
- Advanced retry logic per ISP: PowerMTA's per-domain retry sequences and connection pool controls provide significantly more granular ISP-specific delivery optimisation than Postfix's global retry settings.
Below these thresholds, Postfix correctly configured with OpenDKIM, TLS, and per-ISP concurrency controls provides excellent outbound delivery for commercial email programmes at zero software cost. The configuration documented in this guide produces authenticated, TLS-encrypted delivery with per-ISP rate limiting — sufficient for reliable inbox placement at volumes below the commercial MTA investment threshold. Configure it correctly from initial setup; monitor the mail log for delivery problems; and Postfix will deliver email reliably for as long as the programme needs it to.
Verifying the Complete Postfix Configuration
After completing the Postfix + OpenDKIM + TLS configuration, run a full end-to-end verification before sending production email. The verification sequence:
# Step 1: Check Postfix configuration syntax postfix check # Step 2: Verify OpenDKIM is running and signing systemctl status opendkim # Should show: active (running) # Step 3: Send a test message and check headers echo "Test message body" | mail -s "Postfix deliverability test" test@mail-tester.com # Step 4: Check the mail-tester.com score (submit test email to get the URL) # A correctly configured Postfix installation should score 9-10/10 on mail-tester.com # Step 5: Verify authentication headers in a delivered message # Send test to a Gmail seed address and check full headers: # Authentication-Results should show: # spf=pass dkim=pass (d=brand.com) dmarc=pass
The mail-tester.com score provides the broadest single-step configuration verification -- it checks SPF, DKIM, DMARC, blacklist status, content scoring, and TLS simultaneously. A score below 8/10 on mail-tester.com indicates configuration gaps that will affect deliverability and should be resolved before the first production send.
Production Hardening Checklist
Before using Postfix for any production commercial email sending, verify these additional hardening steps: (1) PTR record set by hosting provider for the sending IP -- required for Microsoft 365 delivery. (2) Firewall allows outbound port 25 from the server to all destinations (many VPS providers block port 25 by default -- request an unblock from the provider). (3) Postfix log rotation configured to prevent disk-filling log accumulation. (4) Daily pflogsumm summary email set up to provide delivery status visibility without real-time log monitoring. (5) Let's Encrypt auto-renewal configured and tested -- a certificate expiry causes TLS failures that produce delivery rejections.
Postfix, configured with the settings documented in this guide, is a production-capable outbound MTA that reliably delivers authenticated commercial email. It is not the most feature-rich option -- that distinction goes to PowerMTA -- but for programmes where open-source infrastructure, zero licensing cost, and straightforward Linux system administration are priorities, Postfix delivers commercial email effectively at the volumes for which it is appropriately scaled.
Common Postfix Deliverability Problems and Fixes
Problem: Mail deferred with "Connection refused" to destination MX -- This typically means port 25 is blocked by the hosting provider or a firewall rule. Check: telnet gmail-smtp-in.l.google.com 25. If connection is refused, contact the hosting provider to unblock outbound port 25 for the server IP.
Problem: dkim=fail in Authentication-Results despite OpenDKIM running -- Verify the DKIM public key in DNS matches the private key in /etc/opendkim/keys/. Check that the selector name in opendkim.conf matches the DNS record subdomain. Verify OpenDKIM socket permissions allow Postfix to connect: ls -la /run/opendkim/opendkim.sock should show group ownership accessible to Postfix.
Problem: Messages deferred with "TLS is required" at destination -- The destination server requires TLS but the Postfix client cannot establish a TLS connection. Check the sending server's TLS certificate is valid and not expired. Verify smtp_tls_security_level = encrypt is set and the CA bundle path is correct for the Linux distribution.
Problem: High deferral rate at Gmail with "421 Try again later" -- Gmail is rate-limiting the sending IP. Reduce gmail.com_destination_concurrency_limit from 20 to 10-12, and ensure the sending IP has built reputation through warmup before attempting high-volume sends. Monitor Postmaster Tools for the sending domain to track reputation building over time.
Postfix troubleshooting requires familiarity with the mail log format -- most problems leave clear diagnostic messages in /var/log/maillog. When a delivery problem appears, the sequence is: (1) identify the affected destination domain from the log, (2) read the SMTP response code and message, (3) cross-reference with the SMTP error code reference to determine whether the response is a permanent failure (5xx), temporary deferral (4xx), or a configuration problem. The mail log is verbose but complete -- every delivery attempt is recorded with the information needed to diagnose the outcome.