Adapting a Postfix Mail Relay to Route Mail to Mailpit for Development

After setting up a Postfix mail relay for production notifications, I needed a way to intercept mail from development environments and send it to Mailpit instead of Mailgun. The goal: dev mails never hit the real relay, without changing application configs each time I switch environments.

Mailpit is a local SMTP server with a web UI - similar in concept to MailHog, but actively maintained and faster. Think of it as curl for email: you throw mail at it and inspect what came out. swaks (Swiss Army Knife for SMTP) is the ideal companion for testing this, covering auth, TLS, headers, and anything else you might want to verify.

sudo apt install swaks
swaks --version

Transport Maps: Route by Domain

The cleanest approach for domain-based routing is Postfix’s transport map. This lets you say “everything to test.local goes here, everything else goes to the default relayhost.”

First, add the transport map directive to /etc/postfix/main.cf:

sudo vim /etc/postfix/main.cf
transport_maps = hash:/etc/postfix/transport

Then create the transport rules in /etc/postfix/transport:

# Routes for Mailpit - all other mail goes to the default relayhost (e.g. Mailgun)
test.local      smtp:[192.0.2.10]:1025
dev.local       smtp:[192.0.2.10]:1025
@test.local     smtp:[192.0.2.10]:1025
example.com     smtp:[192.0.2.10]:1025

Replace 192.0.2.10 with the IP of the host running Mailpit. Note that test.local and @test.local are functionally equivalent - the @domain syntax is the explicit form for matching all addresses at a domain.

With this in place, mail to any of these addresses routes to Mailpit:

  • user@test.local
  • admin@test.local
  • app@dev.local
  • test@dev.local

Compile the map and reload Postfix:

sudo postmap /etc/postfix/transport
sudo systemctl reload postfix

Test it:

swaks --to test@test.local --server localhost

Check Mailpit’s web UI - the message should appear there rather than going through Mailgun.

Dedicated SMTP Port for Dev Traffic

If you want a cleaner separation - all traffic hitting a specific port goes to Mailpit, no domain matching needed - add a custom SMTP service in /etc/postfix/master.cf:

2525 inet n - n - - smtpd
  -o relayhost=[192.0.2.10]:1025

This adds a listener on port 2525 that overrides the default relayhost for anything received on that port. The result:

  • Port 25 → normal routing (Mailgun via your configured relayhost)
  • Port 2525 → always Mailpit, regardless of recipient

Dev servers can then point at smtp://mailrelay.example.internal:2525 and every outbound mail automatically ends up in Mailpit. No per-application env vars, no conditional logic in app code.

Reload Postfix after the change:

sudo systemctl reload postfix

Sender-Based Routing

The third option is routing by sender address. This is useful if you have a fixed set of application accounts that should always use Mailpit, regardless of which host they send from or which port they connect to.

In /etc/postfix/main.cf:

sender_dependent_relayhost_maps = hash:/etc/postfix/sender_relay

Create /etc/postfix/sender_relay:

dev@myapp.local         [192.0.2.10]:1025
@test.local             [192.0.2.10]:1025

Compile and reload:

sudo postmap /etc/postfix/sender_relay
sudo systemctl reload postfix

Any mail from dev@myapp.local or any sender at @test.local gets routed to Mailpit, independent of the recipient or the port used.

Which Approach to Use

These three methods can be combined or used independently:

  • Transport map - best when you control the recipient domain (local/fake TLDs for dev environments)
  • Dedicated port - best when you control the application’s SMTP configuration but not the sender or recipient
  • Sender-based routing - best when you have stable service accounts and want routing to follow the sender identity

For most homelab dev setups, the transport map is the easiest starting point. The dedicated port approach is cleaner if you’re running multiple dev services that might send to unpredictable addresses.

Resources