Dedicated Mail Server Hosting Multiple Domains – Step 3 – Postfix and Dovecot

On a typical Linux mail server mail is stored on the disk either using one big file in /var/mai/%user either in one Maildir in users’s home folder. This Maildir usually contains one small file for each mail stored on your server and some indexing information etc.

In this setup, because users are stored on a virtual manner (in an SQL database) we need to create a user on the system, to which all the Maildirs will belong to.

adduser mails --shell=/usr/sbin/nologin

This will create a user that has “nologin” shell. There’s no need to set a password for it, as we will never use it for SSH login and by default ssh does not allow you to login remotely using empty passwords.

One of the most important sub-steps here is to get this user’s UID. For example I did not create any user on CentOS install, so my new user’s ID is 1000, it also gas GID 1000. This is important becautse your mail server will use this ID to store and access files.

We need to create a new SQL user to be used by Postfix MTA and Dovecot as the one used by Postfix Admin has write permissions and for this one there’s no need. After launching from the shell mysql -p and entering the password:

grant select on postfix.* to postfix@localhost identified by 'POSTFIX_PASSWORD';
flush privileges;

Then we start creating SQL config files for Postfix in /etc/postfix as follows:

mysql_virtual_alias_maps.cf

user = postfix
password = POSTFIX_PASSWORD
hosts = 127.0.0.1
dbname = postfix
query = SELECT goto FROM alias WHERE address='%s' AND active = 1

mysql_virtual_domains_maps.cf

user = postfix
password = POSTFIX_PASSWORD
hosts = 127.0.0.1
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '0' and active = '1'

mysql_virtual_mailbox_maps.cf

user = postfix
password = POSTFIX_PASSWORD
hosts = 127.0.0.1
dbname = postfix
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = 1

mysql_relay_domains_maps.cf

user = postfix
password = POSTFIX_PASSWORD
hosts = 127.0.0.1
dbname = postfix
query = SELECT domain FROM domain WHERE domain='%s' and backupmx = '1'

Then in Postfix’s main.cf

virtual_alias_maps = proxy:mysql:/etc/postfix/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:1000
virtual_mailbox_base = /home/mails
virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
relay_domains = $mydestination, proxy:mysql:/etc/postfix/mysql_relay_domains_maps.cf
virtual_minimum_uid = 1000
virtual_transport = dovecot
virtual_uid_maps = static:1000

and in master.cf add the lines:

dovecot   unix  -       n       n       -       -       pipe
 flags=DRhu user=mails:mails argv=/usr/libexec/dovecot/dovecot-lda -f ${sender} -d ${recipient}

The next step is Dovecot. It is important to set it up as Postfix uses Dovecot LDA to deliver mail. After a simple yum install dovecot dovecot-mysql we need to change directory to /etc/dovecot/conf.d where we edit some files as follows:

10-auth.conf

disable_plaintext_auth = no # some people just don' bother
auth_mechanisms = plain login # Otherwise Windows 8/8.1's Mail app won't authenticate
#!include auth-system.conf.ext # comment this line
!include auth-sql.conf.ext #uncomment this one

15-lda.conf

lda_mailbox_autocreate = yes
lda_mailbox_autosubscribe = yes

10-mail.conf

mail_location = maildir:/home/mails/%u:INDEX=/home/mails/%u/index

/etc/dovecot/dovecot-sql.conf.ext

driver = mysql
connect = host=localhost dbname=postfix user=postfix password=$POSTFIX_PASSWORD

password_query = SELECT password FROM mailbox WHERE username = '%u'
user_query = SELECT username, 1000 AS uid, 1000 AS gid , '/home/mails/%u' as home FROM mailbox WHERE username = '%u'

Instead of 1000 you might put in the UID/GID from the user you created.

Have you added a new domain in your Postfix Admin interface? How about a new user? it is time to send him a test mail.

echo test |mail -s test USER@DOMAIN.COM

Now tail your mailllog. tail /var/log/maillog should show you something like:

Sep  6 12:31:46 mail postfix/qmgr[32482]: C44CD80BBFFE: from=<root@mail.cheriches.com>, size=438, nrcpt=1 (queue active)
Sep  6 12:31:46 mail dovecot: lda(emil@cheriches.com): msgid=<20140906085511.C44CD80BBFFE@mail.cheriches.com>: saved mail to INBOX
Sep  6 12:31:46 mail postfix/pipe[365]: C44CD80BBFFE: to=<emil@cheriches.com>, relay=dovecot, delay=5880, delays=5880/0.02/0/0.22, dsn=2.0.0, status=sent (delivered via dovecot service)

So, right now you have a functional email server, meaning that you are able to send and receive mail, but there’s still a lot of work to be done.

8 thoughts on “Dedicated Mail Server Hosting Multiple Domains – Step 3 – Postfix and Dovecot

  1. Rama October 25, 2014 at 4:09 am

    And if mails user isnt my first user?
    I get this error in mailin file:
    Oct 25 00:42:23 my-vps dovecot: auth: Error: userdb(example@mail.com): client doesn’t have lookup permissions for this user: userdb uid (1000) doesn’t match peer uid (1002) (to bypass this check, set: service auth { unix_listener /var/run/dovecot/auth-userdb { mode=0777 } })
    Oct 25 00:42:23 my-vps dovecot: lda: Error: user example@mail.com: Auth USER lookup failed
    Oct 25 00:42:23 my-vps dovecot: lda: Fatal: Internal error occurred. Refer to server log for more information.
    Oct 25 00:42:23 my-vps postfix/pipe[27566]: BA9AE42461: to=, relay=dovecot, delay=0.14, delays=0.07/0.01/0/0.05, dsn=4.3.0, status=deferred (temporary failure)

    I read that i have to modify /var/run/dovecot/auth-userdb , its true?
    I’ am very newly with this, sory

    • Rama October 25, 2014 at 8:12 pm

      I am a stupid haha, it was only I forget to modify the correct UID for the user mails in /etc/dovecot/dovecot-sql.conf.ext.
      To get the UID for user mails run in ssh: cat /etc/passwd | grep "^mails:" | cut -d":" -f3

  2. Andrew Schott March 20, 2016 at 5:19 am

    Thanks for putting together this guide. But I am running into a roadblock on the test message. I followed the guide up until the end of this page, and then logged into the postfixadmin site and added the virtual domain and virtual user. And yes I do have the DNS setup already as well, its routeable to the host and there is an mx record thats appropriate. I get the following errors in /var/log/maillog:

    Mar 20 05:16:03 mx dovecot: auth-worker(3811): Error: mysql: Query timed out (no free connections for 60 secs): SELECT username, 1000 AS uid, 1000 AS gid , ‘/home/mails/andrew@andrewschott.com’ as home FROM mailbox WHERE username = ‘andrew@andrewschott.com’
    Mar 20 05:16:03 mx dovecot: auth-worker(3811): Error: sql(andrew@andrewschott.com): User query failed: Not connected to database
    Mar 20 05:16:03 mx dovecot: auth: Error: auth worker: Aborted request: Lookup timed out
    Mar 20 05:16:03 mx dovecot: lda: Error: user andrew@andrewschott.com: Auth USER lookup failed
    Mar 20 05:16:03 mx dovecot: lda: Fatal: Internal error occurred. Refer to server log for more information.
    Mar 20 05:16:03 mx dovecot: auth-worker(3821): Error: mysql(localhost): Connect failed to database (postfix): Access denied for user ‘postfix’@’localhost’ (using password: YES) – waiting for 1 seconds before retry
    Mar 20 05:16:03 mx postfix/pipe[3807]: 34CE33CE0: to=, relay=dovecot, delay=60, delays=0.05/0.01/0/60, dsn=4.3.0, status=deferred (temporary failure)
    Mar 20 05:16:04 mx dovecot: auth-worker(3821): Error: mysql(localhost): Connect failed to database (postfix): Access denied for user ‘postfix’@’localhost’ (using password: YES) – waiting for 5 seconds before retry
    Mar 20 05:16:09 mx dovecot: auth-worker(3821): Error: mysql(localhost): Connect failed to database (postfix): Access denied for user ‘postfix’@’localhost’ (using password: YES) – waiting for 25 seconds before retry

    • Emil C March 20, 2016 at 10:41 am

      It looks as you have a problem connecting to the SQL server.

      Try connecting from the shell with “mysql -u postfix -p” then run something like “show processlist;” to see if you really have to many opened connections.

      Another thing to look for is SELinux, try setting it to permissive(“setenforce 0” as root) and see if it works this way.

  3. Andrew Schott March 20, 2016 at 2:24 pm

    Thanks for the tips. Firstly, SELinux is off as this is a vps using some backend that prevents it.

    [root@mx ~]# sestatus
    SELinux status: disabled
    [root@mx ~]#

    As for the mysql stuff, not very familiar with that command, but here it goes. I booted the vps, did the test mail message command (the piped one from above) and then did the show processlist, It looks like this:

    MariaDB [(none)]> show processlist;
    +—-+———+—————–+———+———+——+——-+——————+———-+
    | Id | User | Host | db | Command | Time | State | Info | Progress |
    +—-+———+—————–+———+———+——+——-+——————+———-+
    | 2 | postfix | localhost | NULL | Query | 0 | NULL | show processlist | 0.000 |
    | 3 | postfix | localhost:48496 | postfix | Sleep | 22 | | NULL | 0.000 |
    | 4 | postfix | localhost:48498 | postfix | Sleep | 22 | | NULL | 0.000 |
    | 5 | postfix | localhost:48500 | postfix | Sleep | 22 | | NULL | 0.000 |
    +—-+———+—————–+———+———+——+——-+——————+———-+
    4 rows in set (0.00 sec)

    MariaDB [(none)]>

  4. Emil C March 20, 2016 at 2:27 pm

    You should also run in MySQL:
    show variables like ‘max_connections’;

    • Andrew Schott March 20, 2016 at 2:35 pm

      MariaDB [(none)]> show variables like “max_connections”;
      +—————–+——-+
      | Variable_name | Value |
      +—————–+——-+
      | max_connections | 151 |
      +—————–+——-+
      1 row in set (0.00 sec)

      MariaDB [(none)]>

  5. Frank June 16, 2016 at 1:57 pm

    Hello good day Thank you for your tutorial excelnte mail and domain. But I was a problem with the alias domains : Recipient address rejected : User unknown in virtual mailbox table ; could you help me with this Thanks and Regards

Leave a Reply

Please type the characters of this captcha image in the input box

Please type the characters of this captcha image in the input box
Name *
Email *
Website