Postfix is the most widely used Mail Transfer Agent (MTA) for Linux, handling outgoing email delivery with security and reliability. While full email hosting is complex, setting up Postfix for application email delivery is a common and essential task. This guide covers configuring Postfix for production use from a senior developer's perspective.
Why Postfix
Postfix offers significant advantages:
- Security: Designed with security as a priority
- Performance: Handles high email volumes efficiently
- Modular: Easy to integrate with spam filters and authentication
- Reliable: Battle-tested in production for decades
- Well-documented: Extensive documentation and community support
Installation
CentOS/RHEL
sudo dnf install postfix
sudo systemctl start postfix
sudo systemctl enable postfix
Ubuntu/Debian
sudo apt install postfix
# Select "Internet Site" during configuration
# Enter your domain name when prompted
Verify Installation
# Check status
sudo systemctl status postfix
# Check if listening
sudo netstat -tlnp | grep :25
# Send a test email
echo "Test email body" | mail -s "Test Subject" [email protected]
Basic Configuration
Main Configuration File
/etc/postfix/main.cf:
# Basic settings
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
# Network settings
inet_interfaces = all
inet_protocols = ipv4
# Trusted networks (localhost only for the sending server)
mynetworks = 127.0.0.0/8
# Destination domains
mydestination = $myhostname, localhost.$mydomain, localhost
# Relay settings (empty = no relay)
relayhost =
# Mailbox settings
home_mailbox = Maildir/
# Size limits
message_size_limit = 10485760 # 10 MB
mailbox_size_limit = 0 # Unlimited
# SMTP settings
smtpd_banner = $myhostname ESMTP
biff = no
append_dot_mydomain = no
Apply Changes
# Check configuration syntax
sudo postfix check
# Reload configuration
sudo postfix reload
# Or restart entirely
sudo systemctl restart postfix
TLS/SSL Encryption
Generate Certificates
Using Let's Encrypt (recommended):
sudo certbot certonly --standalone -d mail.example.com
Or self-signed for internal use:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/postfix/ssl/mail.key \
-out /etc/postfix/ssl/mail.crt \
-subj "/CN=mail.example.com"
Configure TLS
Add to /etc/postfix/main.cf:
# TLS settings
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_security_level = may
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# Outbound TLS
smtp_tls_security_level = may
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_loglevel = 1
SMTP Authentication
Install SASL
# CentOS/RHEL
sudo dnf install cyrus-sasl cyrus-sasl-plain
# Ubuntu/Debian
sudo apt install libsasl2-modules sasl2-bin
Configure SASL
Create /etc/postfix/sasl/smtpd.conf:
pwcheck_method: saslauthd
mech_list: PLAIN LOGIN
Add to /etc/postfix/main.cf:
# SASL authentication
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
Relay Through External SMTP
For better deliverability, relay through a trusted provider (SendGrid, Mailgun, AWS SES):
SendGrid Example
Add to /etc/postfix/main.cf:
relayhost = [smtp.sendgrid.net]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
Create /etc/postfix/sasl_passwd:
[smtp.sendgrid.net]:587 apikey:your-sendgrid-api-key
Secure and hash:
sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
sudo systemctl restart postfix
AWS SES Example
relayhost = [email-smtp.us-east-1.amazonaws.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.crt
SPF, DKIM, and DMARC
Essential for email deliverability:
SPF Record
Add a DNS TXT record:
example.com. TXT "v=spf1 mx ip4:YOUR_SERVER_IP include:_spf.google.com ~all"
DKIM with OpenDKIM
# Install
sudo dnf install opendkim opendkim-tools
# Generate keys
sudo opendkim-genkey -D /etc/opendkim/keys/example.com/ -d example.com -s mail
# Configure /etc/opendkim.conf
Domain example.com
Selector mail
KeyFile /etc/opendkim/keys/example.com/mail.private
Socket inet:8891@localhost
# Add to main.cf
milter_protocol = 6
milter_default_action = accept
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
Add a DNS TXT record:
mail._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=<public-key-from-mail.txt>"
DMARC Record
Add a DNS TXT record:
_dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:[email protected]"
Docker Integration
Use Host Postfix from Container
In docker-compose.yml:
services:
app:
image: your-app
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
MAIL_HOST: host.docker.internal
MAIL_PORT: 25
Postfix Container
services:
postfix:
image: boky/postfix
environment:
ALLOWED_SENDER_DOMAINS: example.com
RELAYHOST: "[smtp.sendgrid.net]:587"
RELAYHOST_USERNAME: apikey
RELAYHOST_PASSWORD: your-api-key
ports:
- "1025:587"
Application Integration
PHP (Laravel)
.env:
MAIL_MAILER=smtp
MAIL_HOST=localhost
MAIL_PORT=25
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
[email protected]
MAIL_FROM_NAME="${APP_NAME}"
Node.js (Nodemailer)
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: 'localhost',
port: 25,
secure: false,
tls: {
rejectUnauthorized: false
}
});
async function sendEmail(to, subject, html) {
return transporter.sendMail({
from: '[email protected]',
to,
subject,
html
});
}
Python
import smtplib
from email.mime.text import MIMEText
def send_email(to, subject, body):
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = '[email protected]'
msg['To'] = to
with smtplib.SMTP('localhost', 25) as server:
server.send_message(msg)
Monitoring and Logs
View Mail Queue
# Show queue
sudo postqueue -p
# Flush queue (retry all)
sudo postqueue -f
# Delete all queued mail
sudo postsuper -d ALL
View Logs
# CentOS/RHEL
sudo tail -f /var/log/maillog
# Ubuntu/Debian
sudo tail -f /var/log/mail.log
# Filter for errors
sudo grep -i error /var/log/maillog
Troubleshooting
Lock File Error
fatal: open lock file /var/lib/postfix/master.lock: unable to set exclusive lock
Solution:
sudo fuser /var/lib/postfix/master.lock
sudo kill <process_id>
sudo systemctl start postfix
Test Email Delivery
# Manual SMTP test
telnet localhost 25
EHLO localhost
MAIL FROM:<[email protected]>
RCPT TO:<[email protected]>
DATA
Subject: Test
Test message
.
QUIT
Check Email Headers
Use mail-tester.com to verify SPF, DKIM, and deliverability score.
Key Takeaways
- Start simple: Basic Postfix works for application email
- Use relays: SendGrid/SES for production deliverability
- TLS everywhere: Encrypt all connections
- SPF/DKIM/DMARC: Essential for inbox delivery
- Monitor queues: Check logs regularly
- Test thoroughly: Use mail-tester.com before going live
Postfix handles email delivery reliably—combine it with a reputable relay service for production applications that need guaranteed delivery.