FreeBSD mail server: Virtual users and LMTP

published on in category , Tags: freebsd mail opensmtpd dovecot

In this part of the FreeBSD mail server, we build on the recent two posts where we set up the IMAP and SMTP and extend our configuration to support virtual users, which means that we don’t autenticate with system users anymore and, alongside, hand over the incoming email management to Dovecot by providing email from OpenSMTPd to Dovecot using LMTP. Since both daemons will not work together more closely, we will also unify the login credentials in a single passwd-style file that can be read from both OpenSMTPd and Dovecot.

In this series we will set up a fully-featured mail server in a FreeBSD jail using OpenSMTPd, Dovecot and rspamd. In contrast to many other guides, this one is split into multiple posts that can either be read and followed individually, or as a whole. After each post, you end up with a fully working system (that might lack some features ;)).

Create vmail user

First off, we need to create the system user for vmail, that stores email for all users. For that we need a home directory:

mkdir /var/vmail

Now we use the common adduser command but set a different home directory, use an empty password but directly lock the account after creation since no one is supposed to login in there:

Username: vmail
Full name:
Uid (Leave empty for default):
Login group [vmail]:
Login group is vmail. Invite vmail into other groups? []:
Login class [default]:
Shell (sh csh tcsh nologin) [sh]: nologin
Home directory [/home/vmail]: /var/vmail
Home directory permissions (Leave empty for default):
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]: yes
Lock out the account after creation? [no]: yes
Username   : vmail
Password   : <blank>
Full Name  :
Uid        : 1002
Class      :
Groups     : vmail
Home       : /var/vmail
Home Mode  :
Shell      : /usr/sbin/nologin
Locked     : yes
OK? (yes/no): yes
adduser: INFO: Successfully added (vmail) to the user database.
adduser: INFO: Account (vmail) is locked.
Add another user? (yes/no): no

Finally, we need to set up the permissions for the home directory:

chown -R vmail:vmail /var/vmail/

Set up LMTP and vmail in OpenSMTPd

In the last post, we enabled LMTP as a protocol in Dovecot. If you only start with this very post, please make sure that it is set up correctly.

We first need to install an extension for OpenSMTPd that allows it to consume passwd-style files. Luckily there’s a package for it:

pkg install opensmtpd-extras-table-passwd

Now we can use it in /usr/local/etc/mail/smtpd.conf, so extend the file and put these two new tables directly below the existing ones at the top of the file:

table passwd passwd:/etc/mail/passwd
table virtuals file:/etc/mail/virtuals

We also need to adjust the “local” action (which takes care for incoming mail) to forward everything to Dovecot’s LMTP daemon and to use the newly created virtuals table for lookup. So replace the existing local action, with this new one:

action "local" lmtp "/var/run/dovecot/lmtp" rcpt-to virtual <virtuals>

The last step in this config file is to change the authentication to passwd-table that we just generated. This will allow us to have a central place to store credentials that can be read from both OpenSMTPd and Dovecot. We need to adjust the two listen sections for smtps (SSL/TLS, port 465) and and submission (STARTTLS, port 587) to use passwd table:

listen on lo1 smtps pki auth <passwd>
listen on lo1 port submission tls-require pki auth <passwd>

That’s it with that file, now we only need to add an alias for vmail in /etc/mail/aliases:

vmail: /dev/null

If we’d restart smtpd now, it would yield errors because of the missing files for the passwd and virtuals table that we just defined, so we have to at least create empty files for them first:

touch /etc/mail/{passwd,virtuals}

Now we can restart the smtp server:

service smtpd restart

Set up vmail in Dovecot

Next, we need to do basically exactly the same on Dovecot side: First we will change the authentication type to passwd-file and then, we’ll configure all required paths, both for the authentication file as well as the final mail location.

In /usr/local/etc/dovecot/conf.d/10-auth.conf, exchange the system authentication with passwdfile:

#!include auth-system.conf.ext
!include auth-passwdfile.conf.ext

Then we need to set up the passdb and userdb configuration accordingly in /usr/local/etc/dovecot/conf.d/auth-passwdfile.conf.ext. If you did not change anything before, you can simply replace the whole file with those contents:

passdb {
  driver = passwd-file
  args = scheme=CRYPT /etc/mail/passwd

userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/vmail/%d/%n

Finally, we need to set the mail location for pickup in /usr/local7etc/dovecot/conf.d/10-mail.conf:

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

That’s it already for Dovecot, now we can restart it with:

service dovecot restart

Create virtual user

That’s basically it - all we need to do now is create a user to test the installation. That’s pretty straight forward: First we need to encrypt the password:

smtpctl encrypt

Now just type the plain password and press ENTER. You will be provided with the encrypted password, that we can use in the next step, so copy it and let’s add the entry to /etc/mail/passwd: crypted password goes here::::::

Replace the password and make sure to have six colons at the very end.

Lastly, we need to add the virtual user and map it to the vmail system user in /etc/mail/virtuals: vmail

After restarting smtpd and dovecot to read the configuration again, we’re done:

service dovecot restart
service smtpd restart

Test the setup

Feel free to set up Thunderbird with the new credentials, using the full email adress as username for both IMAP and SMTP and the password you’ve encrypted before.


As with the first two steps, breaking the setup of a mail server down to it’s integral parts, makes it way easier to follow along. To always have a working result at the end of each post, at least for me, gives me the confidence that I will end up with a working system instead of following a long guide and in the end nothing works. By now, we have fully working core functionality and the email is delivered in the final way. In the next steps, we’ll integrate rspamd and take care for correct DKIM and DMARC records. Finally, we’ll learn how to make rspamd learn based on your junk folder and make our TLS certificates renew on a regular cycle.