The purpose of this article is to explain how to create an hight availability email server with Dovecot.
We will use internal plain text files as users backend but it can of course easily be extended to use LDAP or SQL, but this article won’t cover this setup.
Install required packages
On both servers we’ll install dovecot as well as the POP3 and IMAP backends
1 |
apt-get install dovecot-core dovecot-imapd dovecot-pop3d |
To use dovecot clustering feature, known as dsync, we need dovecot 2.2 or later. Debian Jessie’s version is ok.
Setup file-based users database
Edit /etc/dovecot/conf.d/auth-passwdfile.conf.ext and set both userdb and passworddb like this:
1 2 3 4 5 6 7 8 9 10 |
passdb { driver = passwd-file args = scheme=PLAIN username_format=%u /etc/dovecot/users } userdb { driver = passwd-file args = username_format=%u /etc/dovecot/users default_fields = uid=vmail gid=mail home=/srv/vmail/%u } |
I will use plaintext clear password here because I really want to be able to read the users from the configuration file directly. You can of course use an encrypted format, see Dovecot documentation.
The file /etc/dovecot/users will contains the users accounts and we’ll deliver all emails using paths like /srv/vmail/user@domain.com.
Dovecot is set up to always use the vmail user with mail group to avoid uid/gids madness.
First I tried to create a multi-domain setup, using “username_format=%n /etc/dovecot/%d/users” and “default_fields = uid=vmail gid=mail home=/srv/vmail/%d/%n” but current master/master plugin is unable to handle such configuration (Error: passwd-file: User iteration isn’t currently supported with %variable paths) so I decided to use a single authentication file using email as login (%u instead of %n).
We need to create the system user for dovecot:
1 |
adduser --system --ingroup mail --uid 500 vmail --home /srv/vmail |
Now we need to enable this backend by commenting auth-system and un-commenting auth-passwdfile from /etc/dovecot/conf.d/10-auth.conf
1 2 3 4 5 6 7 |
#!include auth-system.conf.ext #!include auth-sql.conf.ext #!include auth-ldap.conf.ext !include auth-passwdfile.conf.ext #!include auth-checkpassword.conf.ext #!include auth-vpopmail.conf.ext #!include auth-static.conf.ext |
Configure Postfix to use Dovecot as delivery agent
In /etc/postfix/master.cf add the following section:
1 2 3 |
# Dovecot LDA dovecot unix - n n - - pipe flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/dovecot-lda -f ${sender} -a ${original_recipient} -d ${user}@${nexthop} |
Then run the following command to make sure Postfix is configured correctly (postconf is a command that will edit main.cf config file):
1 2 |
postconf -e "myhostname=`hostname -f`" postconf -e "mydestination=`hostname -f`, `hostname -s`.localdomain, `hostname -s`, localhost.`hostname -d`, localhost.localdomain, localhost" |
Please MAKE SURE your /etc/hosts and /etc/hostname are configured correctly !
The following commands should return short/full/domain names:
1 2 3 |
hostname -s hostname -f hostname -d |
Now we’ll enable Dovecot LDAP and enable our mail domain:
1 2 3 |
postconf -e virtual_transport="dovecot" postconf -e dovecot_destination_recipient_limit=1 postconf -e virtual_mailbox_domains=domain.com |
Additional Dovecot config
In /etc/dovecot/conf.d/10-mail.conf set
1 |
mail_location = maildir:~/Maildir |
It will deliver emails in Maildir format like this: /srv/vmail/user@domain.com/Maildir
In /etc/dovecot/conf.d/10-auth.conf we’ll enable plain text login because we don’t care about SSL and stuff (non-encrypted auth is disabled for any host except localhost by default):
1 |
disable_plaintext_auth = no |
Create first user and try it
Create /etc/dovecot/users with the following content:
1 |
test@domain.com:{plain}testpassword:::: |
And secure the file permissions:
1 2 |
chown root:dovecot /etc/dovecot/users chmod 640 /etc/dovecot/users |
Finally restart dovecot, postfix and send a test email:
1 2 3 |
systemctl restart dovecot systemctl restart postfix echo test | mail -s test test@domain.com && tail -f -n 20 /var/log/syslog |
You should see something like this in the logs:
1 2 3 4 5 6 |
Mar 29 10:16:40 smtp1 postfix/pickup[26046]: 0620580AE772: uid=0 from=<root> Mar 29 10:16:40 smtp1 postfix/cleanup[26052]: 0620580AE772: message-id=<20160329101640.0620580AE772@smtp1.service.domain.com> Mar 29 10:16:40 smtp1 postfix/qmgr[26047]: 0620580AE772: from=<root@smtp1.service.domain.com>, size=339, nrcpt=1 (queue active) Mar 29 10:16:40 smtp1 dovecot: lda(test@domain.com): msgid=<20160329101640.0620580AE772@smtp1.service.domain.com>: saved mail to INBOX Mar 29 10:16:40 smtp1 postfix/pipe[26055]: 0620580AE772: to=<test@domain.com>, relay=dovecot, delay=0.04, delays=0.02/0.01/0/0.02, dsn=2.0.0, status=sent (delivered via dovecot service) Mar 29 10:16:40 smtp1 postfix/qmgr[26047]: 0620580AE772: removed |
The key part here is dovecot: lda(test@domain.com): msgid=
We can now check what happened on the filesystem:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
root@smtp1.service.domain.com:~# find /srv/vmail/ /srv/vmail/ /srv/vmail/test@domain.com /srv/vmail/test@domain.com/Maildir /srv/vmail/test@domain.com/Maildir/cur /srv/vmail/test@domain.com/Maildir/new /srv/vmail/test@domain.com/Maildir/new/1459247005.M110518P26261.smtp1.service.domain.com,S=412,W=423 /srv/vmail/test@domain.com/Maildir/tmp /srv/vmail/test@domain.com/Maildir/dovecot.index.log /srv/vmail/test@domain.com/Maildir/dovecot-uidvalidity.56fa579d /srv/vmail/test@domain.com/Maildir/dovecot-uidvalidity /srv/vmail/test@domain.com/Maildir/dovecot-uidlist /srv/vmail/test@domain.com/Maildir/dovecot.index.cache |
Now we can test IMAP login will the following transcript using telnet:
1 |
telnet 127.0.0.1 143 |
1 2 3 4 |
. LOGIN test@domain.com testpassword . EXAMINE INBOX . FETCH 1 BODY[] . LOGOUT |
You should see the message body containing “test”. If so, we now have a fully working email server.
Enable doveadm service and replication plugin
Create a new file /etc/dovecot/local.conf with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# Doveadm (used by sync service) service doveadm { inet_listener { # any port you want to use for this: port = 2727 } } doveadm_port = 2727 doveadm_password = mysecretpasswordsharedamongservers # Fix permissions for vmail user service aggregator { fifo_listener replication-notify-fifo { user = vmail group = root mode = 0660 } unix_listener replication-notify { user = vmail group = root mode = 0660 } } |
Then we’ll configure the peer address for replication plugin in /etc/dovecot/conf.d/90-plugin.conf:
1 2 3 |
plugin { mail_replica = tcp:5.6.7.8:2727 } |
Now we will globally enable the replication plugin as well as the notify one (required), in /etc/dovecot/conf.d/10-mail.conf:
1 |
mail_plugins = notify replication |
And that’s it… Yes, really, we’re done here !
Replicate config to secondary server
Here is my synchronisation script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!/bin/sh me="1.2.3.4" peer="5.6.7.8" # Postfix rsync -avz --delete /etc/postfix/ root@${peer}:/etc/postfix/ ssh root@${peer} 'postconf -e "mydestination=`hostname -f`, `hostname -s`.localdomain, `hostname -s`, localhost.`hostname -d`, localhost.localdomain, localhost"' ssh root@${peer} 'postconf -e "myhostname=`hostname -f`"' rsync -vz /etc/aliases root@${peer}:/etc/aliases ssh root@${peer} newaliases systemctl restart postfix ssh root@${peer} systemctl restart postfix sleep 1 ssh root@${peer} systemctl status postfix # Dovecot rsync -avz --delete /etc/dovecot/ root@${peer}:/etc/dovecot/ ssh root@${peer} "sed -i \"s|mail_replica = tcp:${peer}|mail_replica = tcp:${me}|\" /etc/dovecot/conf.d/90-plugin.conf" systemctl restart dovecot ssh root@${peer} systemctl restart dovecot sleep 1 ssh root@${peer} systemctl status dovecot |
Basically it sync the whole Postfix and Dovecot postfix, replace the hostname by the secondary server one in Postfix configuration and change the address in Dovecot’s mail_replica setting.
You can now run echo test | mail -s test test@domain.com on both server and check that both filesystems are updated with all emails 🙂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
root@smtp1.service.domain.com:~# find /srv/vmail/ /srv/vmail/ /srv/vmail/test@domain.com /srv/vmail/test@domain.com/Maildir /srv/vmail/test@domain.com/Maildir/cur /srv/vmail/test@domain.com/Maildir/new /srv/vmail/test@domain.com/Maildir/new/1459247005.M110518P26261.smtp1.service.domain.com,S=412,W=423 /srv/vmail/test@domain.com/Maildir/new/1459248844.M932607P26622.smtp1.service.domain.com,S=412,W=423 /srv/vmail/test@domain.com/Maildir/new/1459249870.M304816P27522.smtp1.service.domain.com,S=412,W=423 /srv/vmail/test@domain.com/Maildir/new/1459250003.M334397P27770.smtp1.service.domain.com,S=412,W=423 /srv/vmail/test@domain.com/Maildir/new/1459250051.M437424P14567.smtp2.service.domain.com,S=436,W=447 /srv/vmail/test@domain.com/Maildir/tmp /srv/vmail/test@domain.com/Maildir/dovecot.index.log /srv/vmail/test@domain.com/Maildir/dovecot-uidvalidity.56fa579d /srv/vmail/test@domain.com/Maildir/dovecot-uidvalidity /srv/vmail/test@domain.com/Maildir/dovecot-uidlist /srv/vmail/test@domain.com/Maildir/dovecot.index.cache |
Of course, you can now connect two Thunderbird instances against 1.2.3.4 and 5.6.7.8 and then create folder, move emails, toggle read flag. Both will show the change with a very little delay.
Thanks for reading and I hope that will help