The most surprising thing about setting up a production-ready SMTP server is how much of the complexity isn’t about sending mail, but about not sending spam.
Let’s see Postfix in action. Imagine a typical web application that needs to send transactional emails: password resets, order confirmations, etc. Instead of each app instance running its own SMTP client, they all send mail to a central Postfix server.
# From a web app instance:
echo "Subject: Your Order #12345\n\nThank you for your order!" | sendmail -f webapp@example.com recipient@their-domain.com
# Postfix server receives this, queues it, and then attempts delivery.
This Postfix server acts as a "smarthost" or "relay host." It receives mail from internal applications and then handles the complexities of delivering it to external mail servers.
The core problem Postfix solves is reliably getting email from one place to another. But in production, "reliably" means battling spam filters, ensuring deliverability, and managing your server’s reputation.
Here’s a look at a stripped-down but functional main.cf for a production Postfix server, focusing on security and deliverability:
# /etc/postfix/main.cf
# Basic Identifiers
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
# Network Configuration
inet_interfaces = all
# For a dedicated relay host, you might use:
# inet_interfaces = localhost
# Or bind to specific IPs:
# inet_interfaces = 192.168.1.100
# Mail Storage
home_mailbox = Maildir/
# Security & Authentication
smtpd_tls_cert_file=/etc/ssl/certs/postfix.pem
smtpd_tls_key_file=/etc/ssl/private/postfix.key
smtpd_tls_security_level = may
smtp_tls_security_level = may
smtpd_tls_loglevel = 1
smtp_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# SASL Authentication (if Postfix accepts mail from external clients, e.g., mobile apps)
# smtpd_sasl_auth_enable = yes
# smtpd_sasl_type = dovecot
# smtpd_sasl_path = private/auth
# smtpd_sasl_security_options = noanonymous
# smtpd_sasl_local_domain = $myhostname
# broken_sasl_auth_clients = yes # For older clients
# Access Control
# Relay restrictions: only allow mail from trusted internal networks
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 192.168.0.0/24
# Recommended for preventing open relay
smtpd_relay_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination
# Rate Limiting (crucial for preventing abuse and spam)
# queue_min_free_disks = 1
# maximal_queue_lifetime = 3d
# bounce_queue_lifetime = 1d
# smtp_generic_maps = hash:/etc/postfix/generic # For rewriting sender addresses
# smtpd_client_restrictions =
# permit_mynetworks
# permit_sasl_authenticated
# reject_invalid_hostname
# reject_non_fqdn_hostname
# reject_unknown_client_hostname
# reject_unknown_sender_domain
# # Rate limiting per client IP
# hash:/etc/postfix/client_rate_limit
# smtpd_sender_restrictions =
# reject_unknown_sender_domain
# reject_non_fqdn_sender
# reject_sender_login_mismatch
# # Rate limiting per sender
# hash:/etc/postfix/sender_rate_limit
# smtpd_recipient_restrictions =
# permit_mynetworks
# permit_sasl_authenticated
# reject_unauth_destination
# reject_non_fqdn_recipient
# reject_unknown_recipient_domain
# # Rate limiting per recipient (use with caution)
# # hash:/etc/postfix/recipient_rate_limit
# DNS Blacklist Checks (essential for spam prevention)
# Requires `postscreen` or direct integration with RBLs
# For direct integration:
# smtpd_client_restrictions = ..., reject_rbl_client zen.spamhaus.org, ...
# Virtual Domains (if Postfix handles multiple domains)
# virtual_mailbox_domains = hash:/etc/postfix/virtual_domains
# virtual_mailbox_maps = hash:/etc/postfix/virtual_mailboxes
# Logging
maillog_file = /var/log/mail.log
The smtpd_tls_cert_file and smtpd_tls_key_file point to your SSL/TLS certificates. smtpd_tls_security_level = may means Postfix will try to use TLS if the client offers it, but won’t force it. For stricter outbound security, smtp_tls_security_level = encrypt is better.
mynetworks is critical. It defines which IP addresses Postfix considers "trusted" and will allow to relay mail without further authentication. This is your internal network.
smtpd_relay_restrictions dictates who can send mail through Postfix. permit_mynetworks and permit_sasl_authenticated allow mail from your internal IPs and authenticated users, respectively. reject_unauth_destination is the catch-all that prevents your server from becoming an open relay for the world.
Rate limiting, using directives like smtpd_client_restrictions and smtpd_sender_restrictions (often populated by helper tools like postfwd or by directly listing RBLs), is your primary defense against abuse and helps maintain a good IP reputation. For instance, hash:/etc/postfix/client_rate_limit would point to a file containing entries like:
# /etc/postfix/client_rate_limit
192.168.1.0/24 rate=50/hour
203.0.113.10 rate=10/hour
This limits clients from 192.168.1.0/24 to 50 mail submissions per hour, and a specific IP 203.0.113.10 to 10 per hour.
The true power often comes from integrating with external services. For example, using postscreen (a separate Postfix daemon) to pre-filter mail before it even hits the main smtpd process. postscreen can perform RBL checks, rate limiting, and greylisting, significantly reducing the load on your main Postfix server and blocking a lot of spam at the earliest possible stage.
When you configure smtpd_recipient_restrictions with reject_rbl_client zen.spamhaus.org, Postfix queries the Spamhaus DNSBL for the client’s IP address. If the IP is listed, Postfix rejects the connection immediately, saving resources and preventing spam from ever reaching your mail queues.
The one thing most people don’t realize is how much of "sending email" is actually about "proving you’re not a spammer." This involves correctly configuring SPF, DKIM, and DMARC records in your DNS, and ensuring your Postfix server is set up to sign outgoing mail with DKIM. Postfix itself can be configured to sign mail using tools like opendkim, which integrates as a content filter.
Once you’ve got SPF, DKIM, and DMARC sorted, you’ll then need to configure Postfix to announce your server’s IP address correctly in HELO/EHLO greetings and ensure reverse DNS (PTR records) are properly set up for your mail server’s IP.