Postfix
The Postfix configuration is two lines. The substance is knowing which knobs not to turn.
The wiring
Add to /etc/postfix/main.cf:
virtual_mailbox_domains = lists.example.com
virtual_transport = lmtp:[127.0.0.1]:8024
virtual_mailbox_domains— the list domain Postfix should accept mail for and route onward.virtual_transport = lmtp:[127.0.0.1]:8024— hand those messages to xListman’s LMTP server. The[127.0.0.1]bracket syntax pins the LMTP client to that host so no MX lookup happens.
Then reload Postfix:
sudo postfix reload
What not to do
- Don’t put the list domain in
mydestination. That makes Postfix treat it as a local domain and applylocal_recipient_maps, which rejects addresses likedev-subscribe@…as unknown local users before xListman ever sees them. - Don’t put it in
relay_domains. Relay semantics are wrong for a final-delivery agent; this is your final delivery. - Don’t rely on
recipient_delimiter. Postfix’s LMTP client does not apply it — it passes the fulldev+role@domainenvelope recipient through, and xListman parses the role suffix itself. That’s exactly what you want.
The bounce reverse leg
The VERP bounce path works only when your MTA accepts a bad recipient at SMTP
time and then fails at final delivery, so a DSN (with the null envelope sender MAIL FROM:<>, per RFC 3464) bounces back to the VERP address. A handy trigger
for testing: an aliases entry mapping a subscribed address to a user that doesn’t
exist.
# /etc/aliases
deadbeat: no_such_local_user_xyz
The address is accepted at RCPT (250), then local(8) fails at delivery — the
DSN routes through LMTP back to xListman’s bounce handler, and the subscription’s
bounce count increments. Bounce it past the list’s bounce_threshold and the
subscription disables, which is the full loop working end to end.
Binding the LMTP server
The LMTP server is unauthenticated, so in production make sure lmtp.listen in
your xListman config binds a private interface:
lmtp:
listen: "127.0.0.1:8024"
so only Postfix on the same host can reach it.
Next: exim.