After messing with my email setup for virtual domain support, I decided I should really move from the old mbox format to Maildir.

Debian

This requires changes to exim, dovecot and the maildrop ~/.mailfilter file.

Exim

To turn on Maildir delivery, add a macro definition to /etc/exim4/exim4.conf.localmacros:

LOCAL_DELIVERY=maildir_home

Update the configuration and restart Exim4:

sudo update-exim4.conf.template -r && sudo update-exim4.conf
sudo systemctl restart exim4

Exim4 will now deliver mail to ~/Maildir for local users.

To enable Maildir delivery for virtual domains, the virtual_domains router was changed to this:

# Router for virtual domains
# Use of domain_data to avoid using tainted data
# Data is considered tainted if if comes from the mail

virtual_domains:
	debug_print = "R: virtual_domains for $local_part@$domain"
	driver = accept
	domains = dsearch;/home/vmail
	local_parts = wildlsearch;/home/vmail/${domain_data}/aliases
	user = vmail
	group = vmail
	router_home_directory = /home/vmail/${domain_data}/${local_part_data}
	transport = maildir_home

# Fail mail to non-existent users for virtual domains
virtual_domains_fail:
    driver = redirect
    allow_fail = yes
    domains = dsearch;/home/vmail
    data = :fail:

The virtual_mailboxes transport is no longer required as the virtual_domains router can directly invoke the maildir_home transport, after setting up the environment correctly.

I’d got the priority of the virtual_domains router wrong in the original setup. By putting the virtual_domains router before before the smarthost router (defined in /etc/exim4/conf.d/router/200_exim4-config_primary, the supported virtual domains do not need to be added to the list of local domains. The fact that a directory exists in /home/vmail is sufficient.

Dovecot

Change the setting of mail_location in the file /etc/dovecot/conf.d/10-mail.conf:

mail_location =
maildir:~/Maildir:INDEX=~/Maildir/index:CONTROL=~/Maildir/control

Maildrop

The ~/.mailfilter file needed a small change to enable Maildir delivery. If the target of to is a directory, maildrop assumes a Maildir format. This is an example of what the filter looks like now:

MAILDIR="$HOME/Maildir"

if ( /freebsd-stable/:h || /freebsd-security/:h )
  to ${MAILDIR}/.Archive.${MATCH}

Migration

In order to convert from the current mbox-with-added-Maildir setup, I first made the Maildir configuration changes on opal (the backup server), used imapsync to copy the existing mail setup from ash to opal, then made the configuration changes on ash and performed a reverse sync. Once all looked good on ash, I could delete the old ~/mail content.

OpenSMTPD

For systems running OpenSMTPD (OpenBSD and Oracle Linux), the dovecot changes are the same. However, the action for mail delivery (as defined in smptd.conf.local) is now:

action "local"	maildir  alias <aliases>

# virtual domain email delivery
action "vmail" \
	maildir "/home/vmail/%{dest.domain}/%{dest.user:lowercase|strip}/Maildir" \
	virtual <virtual_users>

Caveats

A small downside of this approach is reading incoming mail using command-line mail. GNU mail handles Maildir format, as can s-nail on Oracle Linux, but OpenBSD mail does not. So, on OpenBSD, I’ve installed s-nail from ports to enable command-line mail.

I use maildrop for filtering on hydrus mail, but should I want to allow filtering for virtual domains, I’d probably need to use Dovecot’s LMTP for local delivery so that I can use the Pigeonhole sieve capability.