How to install a complete mailserver on Debian 8/9, featuring Postfix, Dovecot, MySQL, Spamassassin, ClamAV, Roundcube and Fail2ban.
~ the howto that actually works ~
Part 1: Introduction
Part 2: Preparations: Apache, Let’s Encrypt, MySQL and phpMyAdmin
Part 3: MTA: Postfix
Part 4: IMAP server: Dovecot
Part 5: Web interface: Roundcube
Part 6: Spam filtering: SpamAsasssin
Part 7: Antivirus: ClamAV and ClamSMTP
Part 8: Quota and other Roundcube settings
Part 9: Using mail with a remote IMAP client (i.e. Thunderbird)
Part 10: Counter brute-force attacks with Fail2ban
Part 11: Sources, config files, colouring and comments
On this page
Installing
Troubleshooting
Set mail location
Enable tranfer of mail delivery from Postfix to Dovecot
Have Dovecot look up accounts in MySQL rather than PAM
Comments are on the last page.

Installing
Install Dovecot with
# aptitude install dovecot-core dovecot-imapd dovecot-lmtpd dovecot-managesieved dovecot-mysql dovecot-sieve
Dovecot is configured modularly with files in /etc/dovecot/conf.d/ being included from /etc/dovecof/dovecof.conf.

Troubleshooting
Keep an eye on the current configuration with doveconf -n, which shows any settings with non-default values:
# watch -d doveconf -n
This is optional but will help troubleshooting.
First we’ll enable a lot of logging options for Dovecot. You can turn most of them off once everything works but this too will greatly ease your troubleshooting effort.
Open /etc/dovecot/conf.d/10-logging.conf. Do read the commentary in that file and any other configuration file you stick your fingers in.
Uncomment/change:
syslog_facility = mail auth_verbose = yes auth_verbose_passwords = yes auth_debug = yes auth_debug_passwords = yes mail_debug = yes verbose_ssl = yes
After saving do
# doveconf reload
Set mail location
In /etc/dovecot/conf.d/10-mail.conf edit:
mail_location = maildir:/var/mail/vmail/%d/%n
This will set the mail storage format to maildir and store all mail under /var/mail/vmail/domainname/username/, e.g. /var/mail/vmail/example.com/tinus/.
mail_privileged_group = vmail
This is the group used for creating mailbox directories
Reload the config with
# doveconf reload
Enable tranfer of mail delivery from Postfix to Dovecot
Unfortunately we can’t separate lmtp from sasl. Sasl is sort of a security add-on to lmtp. While you definitely should use it it would have been nice to be able to set it up (and thus test) it separately. So we’re doing both at once.
Normally Postfix would take care of the delivery of mail to users’ mailboxes. We’re now going to use lmtp to transfer that responsibility to Dovecot. From Wikipedia: “The Local Mail Transfer Protocol (LMTP) is a derivative of ESMTP, the extension of the Simple Mail Transfer Protocol (SMTP). (…) LMTP is designed as an alternative to normal SMTP for situations where the receiving side does not have a mail queue ” It’s mail transport between Postfix and Dovecot.
SASL is a method for allowing Postfix to use Dovecot’s authentication mechanisms. From Wikipedia: “Simple Authentication and Security Layer (SASL) is a framework for authentication and data security in Internet protocols. ” We need to tell Dovecot and Postfix to only accept each other’s (and mail clients’) mail to prevent unauthorized mails from being sent.
In /etc/postfix/main.cf add:
virtual_transport = lmtp:unix:private/dovecot-lmtp smtpd_sasl_auth_enable = yes smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth
The private/dovecot-lmtp directory is relative to /var/spool/postfix/.
virtual_transport tells Dovecot to use the transport protocol lmtp.
/etc/postfix/master.cf describes who is allowed to use sasl:
smtp inet n - - - - smtpd submission inet n - - - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o smtpd_relay_restrictions=permit_sasl_authenticated,reject lmtp unix - - - - - lmtp
Note: the -o lines must start with at least one empty space. Master.cf consists of ‘logical lines’ which can span multiple real lines. A real line is ended by a line ending; a logical line is ended after the last continuation’s line ending. A continuation is defined by one or more whitespaces at the start. It’s easiest to show this with an example:
One real line:
submission inet n - - - - smtpd
One logical line:
submission inet n - - - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt
So be careful to start continuation lines with at least one space and also the reverse: for example when uncommenting the first of a logical line be careful not to leave a whitespace in front of it or the line is seen as a continuation of the previous line resulting in all sorts of confusion.
In /etc/dovecot/conf.d/10-auth.conf:
auth_mechanisms = plain login
This will allow clients to log in with a password.
Open /etc/dovecot/conf.d/10-master.conf. Pay close attention; I always get this wrong, resulting in “no SASL methods available” errors. Use doveconf -n to check for more verbose error messages. If things break there is a good chance you removed or added a hash sign somewhere which caused a clause not to close, like this:
service blah { option { <----------- The error is here... option 1 { option 2 { key_one = value_one key_two = value_two } option 3 { } } service doh { <----------- ...but the log file may mention this line. }
The above code contains six opening and four closing brackets. Not good.
The whole file, while well intended, is easy to screw up. Because a missing closing bracket on a certain line is not formally an error the error you will get is about “service doh” not being a valid option.
So before you start making changes to this file create a backup copy first and very carefully watch and count opening and closing brackets.
/etc/dovecot/conf.d/10-master.conf should contain the following lines:
service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600 user = postfix group = postfix } } service auth { unix_listener /var/spool/postfix/private/auth { mode =0666 user = postfix group = postfix } }
Comment out:
#unix_listener auth-userdb { #mode = 0666 #user = #group = #}
Check with
# doveconf -n
for error messages before you continue.
If /var/log/mail.log says “no SASL methods available” you probably made an error in /etc/dovecot/conf.d/10-master.conf.
Note that at this point you cannot receive mail because we haven’t told Dovecot to check for useraccounts in the database yet. It is looking for local accounts and those don’t exist. This is normal.
Have Dovecot look up accounts in MySQL rather than PAM
In /etc/dovecot/conf.d/10-auth.conf comment out !include auth-system.conf.ext and uncomment !include auth-sql.conf.ext:
#!include auth-system.conf.ext !include auth-sql.conf.ext
/etc/dovecot/dovecot-sql.conf.ext defines how Dovecot can find its way around in the database:
driver = mysql connect = host=127.0.0.1 dbname=postfix user=mailman password=P@ssw0rd default_pass_scheme = SHA512-CRYPT password_query = \ SELECT email as username, pwd AS password \ FROM addresses WHERE email = '%u' user_query = \ SELECT 5000 AS uid, 5000 as gid, email, \ '/var/mail/vmail/%d/%n' AS home \ FROM addresses WHERE email = '%u' iterate_query = SELECT email AS user FROM addresses
If you read the dovecot-sql.conf.ext commentary you may find that you can use static user queries. With static user queries all users lookups return the same uid and gid, which is what we need because of the dedicated account we use for permissions on the mail. So why pull it from the database with a query, you may ask. Because this is a so called dynamic user query. Dynamic user queries are necessary if you want to be able to use the iteration query. (You want to use the iteration query.)
The iterate query is necessary for certain Dovecot commands like searching for mail for management purposes, such as automatically deleting spam older than thirty days or compacting mailboxes.
Let me break down the user query because we’re doing a not very common thing here.
SELECT 5000 AS uid, 5000 as gid means: get the value for ‘uid’ from the table but pretend it is 5000. Do the same for the gid. (In fact we have no uid nor a gid field in the database.)
email: well we definitely want to pull the e-mail address from the database.
‘/var/mail/vmail/%d/%n’ AS home: get the value for ‘home’ from the table but pretend it is ‘/var/mail/vmail/’.
Dovecot replaces %d, %n and %u on the fly. For more info on this fun feature check out http://wiki.dovecot.org/DovecotFeatures#User.2BAC8-Password_Databases.
Do
# dovecot reload && postfix reload
… while keeping an eye on /var/log/mail.log for possible error messages or warnings.
Test if you can still receive and send mail.
Error: warning: connect to mysql server 127.0.0.1: Access denied for user ‘mailman’@’localhost’ (using password: YES)
Make sure you are using the same host, username and password in MySQL, /etc/postfix/mysql_virtual_mailbox_maps.cf and /etc/dovecot/dovecot-sql.conf.ext.
While I was reviewing this article I used P@ssword in one instance and P@ssw0rd in another. I ended up changing everything to P@ssw0rd. To change a user password in MySQL:
# mysql -u root -p mysql> SET PASSWORD FOR 'mailman'@'127.0.0.1' = PASSWORD('P@ssw0rd'); mysql> quit
We’ll do more Dovecot configuration in the next part. Remember, this is a topic based series ;)