exim

The exim configuration is a manualroute router to an LMTP transport, plus one loopback subtlety that trips everyone up.

The router and transport

xlistman_lmtp:
  driver = manualroute
  domains = lists.test
  route_list = * lists.lmtp.local
  transport = xlistman_lmtp

xlistman_lmtp:
  driver = smtp
  protocol = lmtp
  port = 8024
  • Network LMTP in exim uses driver = smtp with protocol = lmtp. The standalone lmtp driver is only for Unix socket or command delivery — not this.
  • The router sends any address in the list domain to the LMTP transport on port 8024.

The loopback subtlety

exim refuses to route mail to its own SMTP interface (127.0.0.1) — a loop guard that no router option overrides. The route target therefore has to be a different local address that isn’t in exim’s local_interfaces.

The standard setup uses a second loopback alias, 127.0.0.2, named in /etc/hosts so no DNS is involved:

sudo ifconfig lo0 alias 127.0.0.2   # one-time loopback setup
# /etc/hosts
127.0.0.2  lists.lmtp.local

Then point the xListman LMTP server at that interface:

lmtp:
  listen: "127.0.0.2:8024"

and deliver outbound mail to exim’s SMTP interface:

smtp:
  host: "localhost"
  port: 2525

Running as a non-root user

A non-root exim instance needs exim_user and trusted_users set so it can do local delivery.

The bounce reverse leg

The same principle as Postfix: a recipient that is accepted at SMTP time but fails at final delivery produces a DSN back to the VERP envelope sender, which routes in through the LMTP transport. In a validation setup that’s an always-failing appendfile transport for a deadbeat@localhost address; in production it’s a real undeliverable address. When the DSN lands, xListman attributes it to the right subscription and counts the bounce.

Next: Pipe mode.