Configuring Postfix for Laravel
Overview
Postfix is a powerful, flexible Mail Transfer Agent (MTA) that handles email routing, delivery, and relay on your server. While Laravel abstracts email sending through its Mail facade, the actual delivery depends on an underlying MTA—and Postfix is one of the most reliable choices for Linux production environments.
This guide provides a comprehensive resource for Laravel developers configuring Postfix, from basic setup to advanced features like virtual domains, TLS security, and policy-based filtering.
Why Postfix for Laravel?
Postfix offers several advantages:
Reliability — Battle-tested in production for decades; widely deployed across the internet
Flexibility — Supports virtual domains, complex routing, relay authentication, and content inspection
Performance — Efficient queue management; scales from single-server to high-volume deployments
Security — Built-in TLS, SASL authentication, and content filtering; runs with minimal privileges
Developer-Friendly — Modular design; can be extended with custom policies and filters
Laravel Integration — Works seamlessly with Laravel's mail system via SMTP on localhost
Documentation Structure
This guide is organized into major sections covering different aspects of Postfix configuration. Each section includes concepts, configuration examples, and practical guidance.
| Section | Coverage |
|---|---|
| Basic Setup | Installation, core parameters, initial configuration |
| Security & TLS | Encryption, certificates, authentication |
| Virtual Domains | Multi-domain hosting, alias mapping |
| SMTP Relay & Access Control | Relay configuration, access policies, rate limiting |
| Content Inspection | Header/body filtering, anti-spam measures |
| Database Lookups | MySQL, PostgreSQL, LDAP integration |
| Troubleshooting | Debugging, queue management, common issues |
| Laravel Integration | Configuration for Laravel Mail system |
Basic Setup
Installation
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install postfix
# Choose: Internet Site (for typical mail server)
sudo systemctl enable postfix
sudo systemctl start postfix
CentOS/RHEL:
sudo yum install postfix
sudo systemctl enable postfix
sudo systemctl start postfix
Verify installation:
postfix -v
postconf -n # Show non-default settings
Core Configuration Parameters
Edit /etc/postfix/main.cf to configure basic settings:
# Hostname and domain
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
# What domains to accept mail for
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
# Where to relay mail for external domains
relayhost = # Leave empty for direct delivery
# Network interfaces to listen on
inet_interfaces = 127.0.0.1, [::1] # Localhost only for app server
# OR for mail server role:
inet_interfaces = all
# Queue directory
queue_directory = /var/spool/postfix
# Trusted users (web server included)
trusted_users = www-data mail root
# Basic logging
maillog_file = /var/log/postfix.log
After modifying configuration:
sudo postfix reload
sudo postfix check # Verify syntax
Security and TLS
Transport Layer Security (TLS)
TLS encrypts SMTP connections, preventing eavesdropping on email transmission. Configure Postfix for both incoming (server) and outgoing (client) TLS.
Generate Self-Signed Certificate (Test/Development)
# Create private key
sudo openssl genrsa -out /etc/postfix/postfix.key 2048
# Create certificate
sudo openssl req -new -x509 -days 365 -key /etc/postfix/postfix.key \
-out /etc/postfix/postfix.crt
# Set permissions
sudo chmod 600 /etc/postfix/postfix.key
sudo chown postfix:postfix /etc/postfix/postfix.key /etc/postfix/postfix.crt
Use Let's Encrypt Certificate (Production)
# Install Certbot
sudo apt-get install certbot
# Get certificate for your domain
sudo certbot certonly --standalone -d mail.example.com
# Postfix runs as 'postfix' user, needs to read certs
sudo setfacl -m u:postfix:rx /etc/letsencrypt/live
sudo setfacl -m u:postfix:rx /etc/letsencrypt/archive
Configure Postfix for TLS
Add to /etc/postfix/main.cf:
# Server-side TLS (incoming connections)
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_received_header = yes
smtpd_tls_loglevel = 1
# Client-side TLS (outgoing connections)
smtp_tls_security_level = may
smtp_tls_loglevel = 1
# Forward secrecy (strong ciphers)
tls_preempt_cipherlist = yes
smtpd_tls_ciphers = high
smtp_tls_ciphers = high
# Session caching
smtpd_tls_session_cache_database = btree:/var/spool/postfix/smtpd_scache
smtp_tls_session_cache_database = btree:/var/spool/postfix/smtp_scache
SASL Authentication
SASL enables authenticated SMTP relay, allowing remote users to send through your server.
Install Cyrus SASL:
sudo apt-get install libsasl2-2 libsasl2-modules-sql
Configure SASL in /etc/postfix/main.cf:
# Enable SASL authentication
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = cyrus
smtpd_sasl_path = smtpd
smtpd_sasl_local_domain = $myhostname
smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options
Create /etc/postfix/sasl/smtpd.conf:
pwcheck_method: auxprop
auxprop_plugin: sql
sql_engine: mysql
sql_hostnames: localhost
sql_database: postfix
sql_user: postfix_user
sql_passwd: postfix_password
sql_select: SELECT password FROM users WHERE username = '%u@%r'
Virtual Domains
Virtual Domain Hosting
Host multiple internet domains on a single Postfix server. Virtual domains can use local files or database lookups.
Virtual Alias Maps (Forwarding)
Forward mail from virtual domains to local users:
/etc/postfix/main.cf:
virtual_alias_domains = example.com, example.net
virtual_alias_maps = hash:/etc/postfix/virtual
/etc/postfix/virtual:
support@example.com local-user@example.com
info@example.net another-user@example.com
@example.com catch-all@example.com
Build the database:
sudo postmap /etc/postfix/virtual
sudo postfix reload
Virtual Mailbox Maps (Non-Unix Accounts)
Deliver mail to non-system users in a virtual mailbox directory:
/etc/postfix/main.cf:
virtual_mailbox_domains = example.com, example.net
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailboxes
virtual_mailbox_base = /var/mail/vhosts
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_transport = virtual
/etc/postfix/virtual_mailboxes:
user@example.com example.com/user/
admin@example.net example.net/admin/
Create directory structure:
sudo mkdir -p /var/mail/vhosts/example.com /var/mail/vhosts/example.net
sudo chown -R 5000:5000 /var/mail/vhosts
sudo chmod 750 /var/mail/vhosts
SMTP Relay and Access Control
Relay Configuration
Control which mail Postfix accepts and relays. This prevents open relay abuse (sending spam through your server).
Restrict Relay to Local Networks
/etc/postfix/main.cf:
# Trust these networks for relay
mynetworks = 127.0.0.0/8, [::1]/128, 192.168.1.0/24
# Set relay restrictions
smtpd_relay_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
defer_unauth_destination
Restrict by Access Lists
/etc/postfix/main.cf:
smtpd_client_restrictions =
permit_mynetworks,
check_client_access hash:/etc/postfix/client_access,
reject_unknown_client_hostname
smtpd_helo_restrictions =
permit_mynetworks,
reject_invalid_helo_hostname,
reject_non_fqdn_helo_hostname
smtpd_sender_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_non_fqdn_sender,
reject_unknown_sender_domain
/etc/postfix/client_access:
# Allow these IPs
192.168.1.100 OK
10.0.0.0/24 OK
# Deny these IPs
203.0.113.0/24 REJECT
# Throttle specific client
203.0.113.5 DEFER_IF_PERMIT Service temporarily unavailable
Build database:
sudo postmap /etc/postfix/client_access
sudo postfix reload
Rate Limiting
Prevent abuse by limiting sender rates:
/etc/postfix/main.cf:
# Connection rate limits
smtpd_client_connection_rate_limit = 10
smtpd_client_recipient_rate_limit = 100
# Message rate limits
default_process_limit = 100
smtp_connect_timeout = 15s
Content Inspection
Header and Body Checks
Filter emails based on content patterns using regular expressions.
Block Emails Containing Bad Words
/etc/postfix/main.cf:
header_checks = regexp:/etc/postfix/header_checks
body_checks = regexp:/etc/postfix/body_checks
/etc/postfix/body_checks:
# Block messages with "badword"
/badword/i REJECT Message contains prohibited content
# Block common phishing attempts
/click.*here.*now/i REJECT Potential phishing content
# Block certain file types
/\.exe\s*$/i REJECT Executable files not allowed
/etc/postfix/header_checks:
# Reject messages without proper From header
/^From:\s*$/ REJECT Missing From header
# Block spoofed domains
/^From:.*@example.net/ REJECT From domain mismatch
Build database:
sudo postmap regexp:/etc/postfix/body_checks
sudo postfix reload
Milter Integration (Advanced Filtering)
Integrate with SpamAssassin or other milters for advanced content filtering.
/etc/postfix/main.cf:
smtpd_milters = inet:localhost:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept # Don't reject if milter fails
Database Lookups
MySQL Lookup Tables
Store virtual aliases and other mappings in MySQL for easier management.
Build Postfix with MySQL support:
% make tidy
% make makefiles CCARGS="-DHAS_MYSQL" AUXLIBS="-lmysqlclient"
% make
% make install
Create database:
CREATE DATABASE postfix;
CREATE TABLE virtual_aliases (
id INT AUTO_INCREMENT PRIMARY KEY,
virtual_email VARCHAR(255) NOT NULL,
real_email VARCHAR(255) NOT NULL,
UNIQUE (virtual_email)
);
INSERT INTO virtual_aliases (virtual_email, real_email) VALUES
('user@example.com', 'local@example.com'),
('admin@example.net', 'admin@example.com');
Configure Postfix lookup:
/etc/postfix/mysql_virtual_alias_maps.cf:
hosts = 127.0.0.1
user = postfix_user
password = postfix_password
dbname = postfix
query = SELECT real_email FROM virtual_aliases WHERE virtual_email = '%s'
/etc/postfix/main.cf:
virtual_alias_domains = mysql:/etc/postfix/mysql_virtual_alias_maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf
PostgreSQL Lookup Tables
Similar to MySQL but using PostgreSQL:
Build with PostgreSQL:
% make tidy
% make makefiles CCARGS="-DHAS_PGSQL" AUXLIBS="-lpq"
% make
% make install
Database configuration similar to MySQL but with postgresql-specific connection strings.
LDAP Lookups
Query LDAP directory for user information:
Build with LDAP support:
% make tidy
% make makefiles CCARGS="-DHAS_LDAP" AUXLIBS="-lldap -llber"
% make
% make install
Advanced Routing
Address Rewriting
Transform email addresses using pattern matching. Useful for normalizing addresses or rewriting to different domains.
/etc/postfix/main.cf:
smtp_generic_maps = hash:/etc/postfix/generic
/etc/postfix/generic:
user@olddomain.com user@newdomain.com
@localdomain.local @domain.example.com
(.*)@internal.example.com $1@external.example.com
Build and reload:
sudo postmap /etc/postfix/generic
sudo postfix reload
Custom Routing (transport_maps)
Route certain domains through specific mail servers or gateways:
/etc/postfix/main.cf:
transport_maps = hash:/etc/postfix/transport
/etc/postfix/transport:
# Route to local mailbox
example.com local:
.example.com local:
# Route through relay server
partner.com smtp:[relay.partner.com]:25
# Use different SMTP port
secure.example.com smtps:[mail.secure.example.com]:465
Build database:
sudo postmap /etc/postfix/transport
sudo postfix reload
Monitoring and Debugging
Queue Management
Check and manage the mail queue:
# List all queued messages
mailq
# OR
postqueue -p
# Delete a specific message (by ID)
postsuper -d MESSAGE_ID
# Delete all messages
postsuper -d ALL
# Hold/release messages
postsuper -h MESSAGE_ID # Hold
postsuper -r MESSAGE_ID # Release
# Force delivery retry
postfix flush
Testing Address Routing
Verify how Postfix will route an address:
postmap -q user@example.com /etc/postfix/virtual
# Output: forwarded address
# Test with a map type
postmap -q @example.com hash:/etc/postfix/virtual
Log Analysis
Monitor Postfix logs for issues:
# View recent activity
tail -f /var/log/mail.log
# Search for specific patterns
grep "from=<user@example.com>" /var/log/mail.log
# Count messages by status
grep "status=" /var/log/mail.log | grep -o "status=[^ ]*" | sort | uniq -c
# Find rejected messages
grep "reject" /var/log/mail.log
# Monitor specific queue ID
grep QUEUE_ID /var/log/mail.log
Enable Debug Logging
For troubleshooting, enable verbose logging:
/etc/postfix/main.cf:
debug_peer_list = example.com
debug_peer_level = 3
Then monitor with:
tail -f /var/log/mail.log
Laravel Integration
Configure Laravel for Local Postfix
Laravel's Mail system can use Postfix running on localhost:
.env file:
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=25
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="noreply@example.com"
MAIL_FROM_NAME="Your Application"
config/mail.php:
'smtp' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST', 'localhost'),
'port' => env('MAIL_PORT', 25),
'encryption' => env('MAIL_ENCRYPTION'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
],
Allow Web Server to Send Mail
Ensure the web server user (www-data) can submit mail to Postfix:
/etc/postfix/main.cf:
# Allow www-data to send mail
trusted_users = www-data
# Accept from localhost
mynetworks = 127.0.0.0/8
Send Test Email from Laravel
Mail::raw('Test message', function($message) {
$message->to('test@example.com')
->subject('Test from Laravel');
});
Check queue:
mailq
Security Best Practices
Run Postfix with Minimal Privileges
Postfix runs as unprivileged postfix user by default. Verify:
ps aux | grep postfix
# Should show: postfix user, not root
Secure Configuration Files
Protect sensitive configuration:
sudo chmod 600 /etc/postfix/main.cf
sudo chmod 600 /etc/postfix/mysql_virtual_alias_maps.cf
sudo chown root:root /etc/postfix/main.cf
Enable SPF, DKIM, DMARC
Prevent email spoofing with DNS records:
SPF Record (in DNS):
v=spf1 mx -all
DKIM (Generate Keys):
openssl genrsa -out dkim_private.key 2048
openssl rsa -in dkim_private.key -pubout -out dkim_public.key
# Add public key to DNS and configure in Postfix
DMARC Record (in DNS):
v=DMARC1; p=quarantine; rua=mailto:admin@example.com
Implement Postscreen
Use Postfix's built-in spam filter:
/etc/postfix/main.cf:
postscreen_enable = yes
postscreen_denylist_action = enforce
postscreen_greylist_action = enforce
Troubleshooting
"Connection refused on port 25"
Postfix isn't running or listening on localhost:
sudo systemctl status postfix
sudo systemctl start postfix
netstat -tulnp | grep postfix
"Relay access denied"
Sender not authorized to relay through server:
# Check mynetworks
postconf mynetworks
# Check SASL auth
postconf -A
"Undeliverable mail" or High Bounce Rate
Check bounce logs:
grep "bounce" /var/log/mail.log
# Investigate specific address
postmap -q user@domain.com hash:/etc/postfix/virtual
"TLS errors" or Certificate Issues
# Verify certificate
openssl x509 -in /etc/postfix/postfix.crt -noout -dates
# Check TLS logs
grep tls /var/log/mail.log
# Test TLS connection
openssl s_client -connect localhost:25 -starttls smtp
Performance Issues or Queue Buildup
# Check queue size
mailq | wc -l
# Check for stuck messages
mailq | grep "^[A-F0-9]"
# Monitor active connections
postqueue -p | grep "active"
# Increase worker processes (in main.cf)
default_process_limit = 100
Postfix Parameters Reference
| Parameter | Purpose |
|---|---|
myhostname |
Server hostname |
mydomain |
Local domain name |
mydestination |
Domains to accept mail for |
relayhost |
External relay server (if any) |
inet_interfaces |
Network interfaces to listen on |
mynetworks |
Trusted networks for relay |
smtpd_tls_cert_file |
TLS certificate path |
smtpd_tls_key_file |
TLS private key path |
virtual_alias_maps |
Virtual alias database |
virtual_mailbox_maps |
Virtual mailbox database |
Common Commands
# Reload configuration
sudo postfix reload
# Check configuration
sudo postfix check
# Restart Postfix
sudo systemctl restart postfix
# View all parameters
postconf
# View specific parameter
postconf -n parameter_name
# Build lookup table
sudo postmap /etc/postfix/filename
# Test mail routing
postmap -q address@example.com hash:/etc/postfix/virtual
# Flush queue (retry deferred)
postfix flush
# View mail queue
mailq
# Stop Postfix
sudo systemctl stop postfix
Next Steps
Basic Setup:
- Install and test local Postfix
- Configure Laravel Mail to use local Postfix
- Test sending emails from Laravel
Production Hardening:
- Install TLS certificates (Let's Encrypt)
- Configure SASL authentication
- Set up SPF, DKIM, DMARC records
- Enable postscreen for spam filtering
Advanced Configuration:
- Set up virtual domains
- Implement database lookups
- Configure content filtering
- Integrate with policy daemons