The very basic SMTP setup now needs IMAP capabilities to be actually usable outside a mutt/shell environment. A bit of research revealed that apparently dovecot is the way to go these days. The following post outlines the steps it took me to get the setup up and running.


The steps necessary to get dovecot up and running:

  • Install dovecot
  • Enable IMAPS in your packet filter
  • Adjust OpenSMTPD to forward incoming mail via lmtp instead of local delivery
  • Create virtual users who share their password between IMAP and SMTP
  • Adjust system defaults to enable smooth dovecot operation
  • Adjust a bunch of aliases and virtual user tables


Dovecot can be installed from packages

pkg_add dovecot

The default installation comes with a master configuration file /etc/dovecot/dovecot.conf which references a bunch of sub-configurations in /etc/dovecot/dovecot/*. While this might be a sane setup for a commercial ISP, it created a lot of confusion on my end. The solution was to create /etc/dovecot/dovecot.conf from scratch, without referencing anything else. Before moving the original out of the way, dovecinf -n provides a good starting point.

Here's my /etc/dovecot/dovecot.conf (just made the hostname somewhat general):

protocols = lmtp imap
postmaster_address =

ssl = yes
ssl_cert = </etc/ssl/
ssl_key  = </etc/ssl/private/
ssl_dh_parameters_length = 4096

passdb {
    args   = scheme=blf-crypt /etc/mail/passwd
    driver = passwd-file
userdb {
    args   = uid=vmail gid=vmail home=/var/mail/%d/%n
    driver = static

mail_location = maildir:/var/mail/%d/%n

A bit of explanation:

  • protocols should be rather self-explanatory, we only care about IMAP (obviously) and LMTP to communicate with the OpenSMTP process. Dovecot supports POP3 etc. as well but I stopped caring about POP many years ago.

  • postmaster_address is somewhat mandatory I read, somwhere, dunno, doesn't hurt.

  • ssl_* is mainly the path to the certificates created earlier. The 4096 Diffie-Hellman takes a while when first starting the daemon, be patient.

  • passdb is the interesting part of the shared password between OpenSMTP and Dovecot, see below.

  • userdb caused quite a bit of head-scratching on my end since most tutorials (see References below) don't care about virtual users too much and some of the early errors in the log suggest using something other than driver = static - don't!

  • mail_location is the place where Dovecot is putting your stuff.


Update /etc/mail/smtpd.conf to reflect the recent changes:

# SSL stuff, see previous post

table aliases  db:/etc/mail/aliases.db
table vusers   file:/etc/mail/vusers
table vdomains file:/etc/mail/vdomains
table passwd   file:/etc/mail/passwd

listen on lo0
listen on egress tls pki auth-optional
listen on egress port submission tls-require pki auth <passwd>

accept for local alias <aliases> deliver to lmtp "/var/dovecot/lmtp" rcpt-to
accept from any for domain <vdomains> virtual <vusers> deliver to lmtp "/var/dovecot/lmtp" rcpt-to
accept from local for any relay

Where this setup differs from most tutorials, it uses db:/etc/mail/aliases.db but I forgot why. Also it does not change the scheme for the password file, stick with file:/... and all will be fine.

Further changes to the base system

Packet Filter

Enable IMAP access through PF:

pass in on $ext_if proto tcp from any to any port imaps

Add vmail user

Handcraft new uid/gid for dovecot:

useradd -g =uid -c "Virtual Mail Users" -d /var/mail -s /sbin/nologin vmail

doas chown vmail:vmail /var/mail/

The latter will cause the security scripts to complain, the ownership could be changed more sophisticatedly, but I want Dovecot to write new directories directly into /var/mail.


Update limits in /etc/login.conf


And run

cap_mkdb /etc/login.conf


Fix /etc/mail/mailname if your hostname is different from your 'MX name'

Virtual Users

Shared Passwords

First, the virtual users need to be created. An example /etc/mail/passwd could look like this:$2b$10$QNie63tsmIR/MfMTx4tAZOjBSGVBA.Pk6BxLHXZdyRgSlnX/SPC9W

This essentially creates the virtual user and her respective password. The username can be anything, I just chose to use something similar to mail-addresses to emphasise the relationship between a virtual user and the account.

There are 2 binaries that can be used to create the password, smptctl encrypt and doveadm pw and they provide pretty much the same result. When using modern ciphers, like Blowfish above, bear in mind that the hash these functions produce for a given input wil not be the same each time you run it. It took a dinosaur from MD5 times (like me), quite a while to figure that one out. It seems important to not use the optional :::::: after the username:password pair, otherwise OpenSMTPD refuses to cooperate (at least in my setup).

Mapping Users

The virtual users need to be mapped to both, actual mail addresses and the dovecot vmail user, an example /etc/mail/vusers would look like that: vmail

This maps two actual mail addresses to the above virtual user and adds a catch-all at the end. The last line maps the virtual user to the dovecot process. That is the part that cost me the most sanity points so far ...


To enable local delivery again, the daemons need to know what's going on. Add the mapping to /etc/mail/aliases

    root: john

Assuming your (the admin) actual (non-virtual) local user is john and is supposed to receive root's mail, this is what does the trick.

The alias database needs to be rebuilt using newaliases.