Postfix

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

Resources