How to Configure Postfix to Save Emails to File Instead of Relaying (Staging Environment Setup)


2 views

When setting up a staging environment that mirrors production, email handling becomes a critical consideration. The goal is to prevent accidental email sends to real customers while maintaining identical application behavior. Postfix provides several ways to achieve this through its flexible configuration system.

There are three primary methods to implement this:

  1. Using always_bcc to duplicate messages to a file
  2. Configuring a virtual mailbox transport
  3. Setting up a local mailbox delivery

This is the simplest approach that works for most staging environments:

# /etc/postfix/main.cf
always_bcc = archive@localhost

# /etc/aliases
archive: "|/usr/bin/formail -a Date: >> /var/log/mail-archive.log"

# Then run:
sudo newaliases
sudo systemctl reload postfix

For more sophisticated setups where you want separate files per recipient:

# /etc/postfix/main.cf
virtual_mailbox_domains = localhost
virtual_mailbox_base = /var/mail/vhosts
virtual_mailbox_maps = hash:/etc/postfix/vmailbox
virtual_minimum_uid = 1000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

# /etc/postfix/vmailbox
@localhost  localhost/catchall/

# Create directory structure
sudo mkdir -p /var/mail/vhosts/localhost/catchall
sudo chown -R postfix:postfix /var/mail/vhosts

For maximum flexibility in processing messages:

# /etc/postfix/main.cf
mailbox_command = /usr/bin/procmail -a "$EXTENSION"

# ~/.procmailrc
:0 c
* ^To:.*@example.com
| /usr/local/bin/archive_email.sh

:0
/dev/null

After configuration, verify with:

sudo postconf -n
sudo systemctl restart postfix
echo "Test email" | mail -s "Test" user@localhost
tail -f /var/log/mail-archive.log

For better visualization of captured emails:

# Install mail viewing utility
sudo apt install mutt

# View the archive
mutt -f /var/log/mail-archive.log

To prevent disk space issues:

# /etc/cron.daily/mail-cleanup
#!/bin/sh
find /var/log/mail-archive.log -type f -mtime +30 -delete

When running a staging environment that mirrors production, one critical challenge is preventing accidental email sends to real customers. The PHP application in this case handles customer communications, and while we could modify the code to use dummy email addresses, maintaining identical code between staging and production is ideal for accurate testing.

The solution involves configuring Postfix to store emails locally rather than relaying them to external servers. Here's how to implement this on Debian/Ubuntu systems:

# Main configuration changes for /etc/postfix/main.cf
inet_interfaces = loopback-only
default_transport = error
local_transport = virtual
virtual_alias_maps = hash:/etc/postfix/virtual

Create a virtual aliases file to catch all outgoing emails:

# /etc/postfix/virtual
@localhost   staging-catchall
@yourdomain.com   staging-catchall

Then compile the aliases and restart Postfix:

sudo postmap /etc/postfix/virtual
sudo systemctl restart postfix

To store emails in Maildir format for easy inspection:

# Add to /etc/postfix/main.cf
virtual_mailbox_domains = localhost, yourdomain.com
virtual_mailbox_base = /var/mail/vhosts
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox
virtual_minimum_uid = 1000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

For simpler needs, you can configure Postfix to only deliver to specific debugging peers:

# /etc/postfix/main.cf
debug_peer_list = example.com
default_transport = discard
local_transport = discard
relay_transport = discard

When using Maildir storage, you can view captured emails using standard mail clients or directly:

ls -l /var/mail/vhosts/yourdomain.com/staging-catchall/new
cat /var/mail/vhosts/yourdomain.com/staging-catchall/new/*

For the mail queue approach mentioned, create a cron job to periodically flush the queue:

# In /etc/crontab
0 * * * * root postsuper -d ALL