In this article, we will show you how to setup and configure a mail server with PostfixAdmin, Postfix, Dovecot and SQLite on a CentOS VPS. PostfixAdmin is a PHP-based web front-end that allows you to manage virtual domains and users for a Postfix mail transport agent. This guide should work on other Linux VPS systems as well but was tested and written for a CentOS 7 VPS.
If you use Ubuntu, follow our tutorial to set up Postfix, Dovecot, Spamassassin, SQLite and PostfixAdmin on an Ubuntu 16.04 VPS with Nginx and PHP 7.0
Table of Contents
1. Update the system and install necessary packages
yum update yum install wget nano sqlite
2. Create system user
For security reasons, we will create a new system user who will be the owner of all mailboxes.
useradd -r -u 150 -g mail -d /var/vmail -s /sbin/nologin -c "Virtual Mail User" vmail mkdir -p /var/vmail chmod -R 770 /var/vmail chown -R vmail:mail /var/vmail
3. Install PostfixAdmin
The latest version of PostfixAdmin, version 3, supports MySQL, PostgreSQL, and SQLite databases. In this guide, we will use SQLite.
Download the PostfixAdmin archive from SourceForge and extract it in the /var/www/html/ directory:
wget -q -O - "http://downloads.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-3.0.2.2/postfixadmin-3.0.2.2.tar.gz" | tar -xzf - -C /var/www/html
Open the mail configuration file and edit the following values:
nano /var/www/html/postfixadmin-3.0.2/config.inc.php
$CONF['configured'] = true; $CONF['database_type'] = 'sqlite'; // $CONF['database_host'] = 'localhost'; // $CONF['database_user'] = 'postfix'; // $CONF['database_password'] = 'postfixadmin'; $CONF['database_name'] = '/var/vmail/postfixadmin.db'; $CONF['domain_path'] = 'NO'; $CONF['domain_in_mailbox'] = 'YES';
chown -R apache: /var/www/html/postfixadmin-3.0.2
Create the SQLite database:
touch /var/vmail/postfixadmin.db chown vmail:mail /var/vmail/postfixadmin.db chmod 660 /var/vmail/postfixadmin.db usermod -a -G mail apache
To populate the database go to https://Your_IP_Address/postfixadmin-3.0.2/setup.php
and you should see something like below:
Testing database connection - OK - sqlite://:xxxxx@//var/vmail/postfixadmin.db
Everything seems fine... attempting to create/update database structure
Create a new admin user:
bash /var/www/html/postfixadmin-3.0.2/scripts/postfixadmin-cli admin add admin@your_domain_name.com --password strong_password22 --password2 strong_password22 --superadmin 1 --active 1
4. Install and configure postfix
Postfix version 3 is not available in the default CentOS 7 repository so we will use the GhettoForge repository:
rpm -Uhv http://mirror.ghettoforge.org/distributions/gf/gf-release-latest.gf.el7.noarch.rpm
Install postfix3 with SQLite support with the command bellow:
yum install postfix3 postfix3-sqlite --enablerepo=gf-plus
Once the installation is completed, create the following files:
nano /etc/postfix/sqlite_virtual_alias_maps.cf
dbpath = /var/vmail/postfixadmin.db query = SELECT goto FROM alias WHERE address='%s' AND active = '1'
nano /etc/postfix/sqlite_virtual_alias_domain_maps.cf
dbpath = /var/vmail/postfixadmin.db query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = '%u' || '@' || alias_domain.target_domain AND alias.active = 1 AND alias_domain.active='1'
nano /etc/postfix/sqlite_virtual_alias_domain_catchall_maps.cf
dbpath = /var/vmail/postfixadmin.db query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = '@' || alias_domain.target_domain AND alias.active = 1 AND alias_domain.active='1'
nano /etc/postfix/sqlite_virtual_domains_maps.cf
dbpath = /var/vmail/postfixadmin.db query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'
nano /etc/postfix/sqlite_virtual_mailbox_maps.cf
dbpath = /var/vmail/postfixadmin.db query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
nano /etc/postfix/sqlite_virtual_alias_domain_mailbox_maps.cf
dbpath = /var/vmail/postfixadmin.db query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = '%u' || '@' || alias_domain.target_domain AND mailbox.active = 1 AND alias_domain.active='1'
[ecko_alert color=”blue”]Stuck somewhere? Get a VPS from us and we’ll do all of this for you, free of charge! We’ll completely set up and configure a mail server for you. [/ecko_alert]
Edit the main.cf
file:
postconf -e "myhostname = $(hostname -f)" postconf -e "virtual_mailbox_domains = sqlite:/etc/postfix/sqlite_virtual_domains_maps.cf" postconf -e "virtual_alias_maps = sqlite:/etc/postfix/sqlite_virtual_alias_maps.cf, sqlite:/etc/postfix/sqlite_virtual_alias_domain_maps.cf, sqlite:/etc/postfix/sqlite_virtual_alias_domain_catchall_maps.cf" postconf -e "virtual_mailbox_maps = sqlite:/etc/postfix/sqlite_virtual_mailbox_maps.cf, sqlite:/etc/postfix/sqlite_virtual_alias_domain_mailbox_maps.cf" postconf -e "smtpd_tls_cert_file = /etc/pki/tls/certs/localhost.crt" postconf -e "smtpd_tls_key_file = /etc/pki/tls/private/localhost.key" postconf -e "smtpd_use_tls = yes" postconf -e "smtpd_tls_auth_only = yes" postconf -e "smtpd_sasl_type = dovecot" postconf -e "smtpd_sasl_path = private/auth" postconf -e "smtpd_sasl_auth_enable = yes" postconf -e "smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination" postconf -e "mydestination = localhost" postconf -e "mynetworks = 127.0.0.0/8" postconf -e "inet_protocols = ipv4" postconf -e "inet_interfaces = all" postconf -e "virtual_transport = lmtp:unix:private/dovecot-lmtp"
Open the master.cf
file, find submission inet n
and smtps inet n
sections and edit as follows:
nano /etc/postfix/master.cf
submission inet n - n - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes # -o smtpd_reject_unlisted_recipient=no # -o smtpd_client_restrictions=$mua_client_restrictions # -o smtpd_helo_restrictions=$mua_helo_restrictions # -o smtpd_sender_restrictions=$mua_sender_restrictions # -o smtpd_recipient_restrictions= -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING smtps inet n - n - - smtpd -o syslog_name=postfix/smtps # -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes # -o smtpd_reject_unlisted_recipient=no # -o smtpd_client_restrictions=$mua_client_restrictions # -o smtpd_helo_restrictions=$mua_helo_restrictions # -o smtpd_sender_restrictions=$mua_sender_restrictions # -o smtpd_recipient_restrictions= -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING
Enable and restart the postfix service
systemctl enable postfix systemctl restart postfix
5. Install and Configure Dovecot
Install dovecot using the command bellow:
yum install dovecot
Open the /etc/dovecot/conf.d/10-mail.conf
file and change the following values:
nano /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:/var/vmail/%d/%n mail_privileged_group = mail mail_uid = vmail mail_gid = mail first_valid_uid = 150 last_valid_uid = 150
Open the /etc/dovecot/conf.d/10-auth.conf
file and change the following values:
nano /etc/dovecot/conf.d/10-auth.conf
auth_mechanisms = plain login #!include auth-system.conf.ext !include auth-sql.conf.ext
Create a new dovecot-sql.conf.ext
file:
nano /etc/dovecot/dovecot-sql.conf.ext
driver = sqlite connect = /var/vmail/postfixadmin.db default_pass_scheme = MD5-CRYPT password_query = \ SELECT username as user, password, '/var/vmail/%d/%n' as userdb_home, \ 'maildir:/var/vmail/%d/%n' as userdb_mail, 150 as userdb_uid, 8 as userdb_gid \ FROM mailbox WHERE username = '%u' AND active = '1' user_query = \ SELECT '/var/vmail/%d/%n' as home, 'maildir:/var/vmail/%d/%n' as mail, \ 150 AS uid, 8 AS gid, 'dirsize:storage=' || quota AS quota \ FROM mailbox WHERE username = '%u' AND active = '1'
In the /etc/dovecot/conf.d/10-ssl.conf
file enable SSL support:
ssl = yes
Open the /etc/dovecot/conf.d/15-lda.conf
file and set the postmaster_address
email address.
postmaster_address = postmaster@your_domain_name.com
Open the /etc/dovecot/conf.d/10-master.conf
file, find the service lmtp section and change it to:
service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600 user = postfix group = postfix } }
find the service auth section and change it to:
service auth { unix_listener /var/spool/postfix/private/auth { mode = 0666 user = postfix group = postfix } unix_listener auth-userdb { mode = 0600 user = vmail #group = vmail } user = dovecot }
Change the service auth-worker section to the following:
service auth-worker { user = vmail }
Set the permissions:
chown -R vmail:dovecot /etc/dovecot chmod -R o-rwx /etc/dovecot
Enable and restart the dovecot service
systemctl enable dovecot systemctl restart dovecot
If everything is setup correctly now you should be able to log in to your PostfixAdmin backend by going to http://Your_IP_Address/postfixadmin-3.0.2.2
and create your first virtual domain and mailbox.
Of course, you don’t have to set up a mail server with PostfixAdmin on CentOS 7, if you use one of our Mail Server Hosting services, in which case you can simply ask our expert Linux admins to setup this for you. They are available 24×7 and will take care of your request immediately.
PS. If you liked this post please share it with your friends on the social networks using the buttons below or simply leave a comment in the Comments Section below. Thanks.
You make so many great points here that I read your article a couple of times. Your views are in accordance with my own for the most part. This is great content for your readers.
hello, I followed your nice tutorial, however cannot install postfix with mysqli support on my centos7 because of missing dependencies:
–> Finished Dependency Resolution
Error: Package: 2:postfix3-3.2.0-1.gf.el7.x86_64 (gf-plus)
Requires: libdb-5.3.so()(64bit)
Error: Package: 2:postfix3-3.2.0-1.gf.el7.x86_64 (gf-plus)
Requires: libicudata.so.50()(64bit)
Error: Package: 2:postfix3-3.2.0-1.gf.el7.x86_64 (gf-plus)
Requires: libsasl2.so.3()(64bit)
Error: Package: 2:postfix3-3.2.0-1.gf.el7.x86_64 (gf-plus)
Requires: libicuuc.so.50()(64bit)
Error: Package: 2:postfix3-3.2.0-1.gf.el7.x86_64 (gf-plus)
Requires: libicui18n.so.50()(64bit)
You could try using –skip-broken to work around the problem
You could try running: rpm -Va –nofiles –nodigest
Did you try to install the missing dependencies?
Helpful article
Got it set up, works nicely, right up until I add more than 10 addresses to a domain, then I get an error (I’ve increased the number of mailboxes to 20).
In http errror_log I see:
[Tue Nov 07 11:56:32.163077 2017] [php7:notice] [pid 44698] [client x.x.x.x:x] caused by query: \n WITH idx AS (SELECT * FROM mailbox \n LEFT JOIN alias ON mailbox.username=alias.address LEFT JOIN vacation ON mailbox.username=vacation.email \n WHERE mailbox.domain=’helpdesk.onemarketing.dk’ \n ORDER BY mailbox.username )\n
SELECT mailbox.username AS label, (SELECT (COUNT(*) – 1) FROM idx t1 WHERE t1.mailbox.username <= t2.mailbox.username) AS row\n FROM idx t2\n WHERE (row % 10) IN (0,9) OR row = 15, referer:
http://y.y.y.y/edit.php?table=mailbox
I experienced the same problem. Here’s a “quick and dirty” fix:
1. Open the “config.inc.php” file in your postfixadmin folder (in my case, it’s here: /var/www/html/postfixadmin-3.0.2/config.inc.php)
2. Search for the “$CONF[‘page_size’]” variable (for me it’s on line 191) and change the number from 10 to something larger. I set mine to 100. It will look like this: $CONF[‘page_size’] = ‘100’;
3. Save the “config.inc.php” file. Refresh your webpage and you’ll now see all of your email addresses.
The longer, nerdier explanation:
In the “functions.inc.php” file, there is a function called “create_page_browser” which checks for the page_size variable and then gets the number of rows from the database. If there are too many rows to fit on one page, PostfixAdmin decides that it needs to select only a few of the rows instead of every row in the table. By default, it shows 10 addresses per page, which is why you got the error after adding more than 10 email addresses.
For us who are following the guide, this is executed on line 517 of “functions.inc.php” under the if statement “if (db_sqlite())”. Inside this statement it uses a query that JOINs the mailbox usernames and aliases, then selects the right amount of rows to show on the page. The problem is that the syntax is wrong and instead of SELECTing a row from the “idx” JOIN, it instead references the database table directly.
So why did I give a “quick and dirty” fix? It seems that despite my best efforts, it throws an error even when the syntax is correct. To test this, I downloaded sqlitebrowser and loaded my *.db file. I tested my syntax and it works. I put that syntax into “functions.inc.php” and it throws an error on the “WITH” statement. Even when I copy and paste the syntax from the error_log and execute it in sqlitebrowser, it works, but for some reason the website just doesn’t like it. Perhaps someone out there can take this info and improve on what I’ve done.
So for now, printing every email address and alias is just fine for me, but if you have hundreds of email addresses, perhaps you’ll need to dig deeper than I did. I hope this helps someone because the guide on this website was the first one I followed after many, many failed attempts at building a simple mail server and the only problem I ran into was this one, so with this fixed, everything works flawlessly. Thank you to the admin who wrote this great guide!
A quick note on step 3: I think you added an extra “.2” to the wget command. I found that v3.0.2 exists and was able to follow the guide fully after changing the parameters like so:
wget -q -O – “http://downloads.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-3.0.2/postfixadmin-3.0.2.tar.gz” | tar -xzf – -C /var/www/html
It may be a bug with the PostfixAdmin installation. You can try to manually change the value in the sqlite database.
I can’t get to postfix admin backend when password is correct
Please check your mail server log files.
Excellent article, but why do we need to use the Ghetto Forge repo to get Postfix 3, when sqlite support was added to Postfix in version 2.8? With the current version in CentOS 7 -Base being 2.10.1, I see no reason to take this extra step. Is there a reason that I am unaware of?
Hi, there is no specific reason for using Postfix 3, both v2 and v3 should work.
Thank you so much for the awesome tutorial! I did install this on my little server and it works fine so far.
I want to highlight a few issues I had that took me some time to troubleshoot for others to pay attention.
1. Don’t use the latest release of Postfixadmin 3.2 as there appears to be a bug with configuring the sqlite database. Keep to 3.0.2 like explained in this guide.
2. When the author says “Edit the main.cf file:”. He doesn’t mean nano /etc/postfix/main.cf but he means just to enter the list of commands inside the terminal. I stupidly added those to the main.cf using nano and I didn’t understand why postfix refused to start
Another recommendation would be to add a letsencrypt certificate using certbot:
certbot -n –agree-tos –standalone certonly -d mail.example.com
Thanks for taking time reading our post and for your suggestions.
Hi,
After setup, tried to send mail but getting error:-
18:15:01 dedi postfix/smtp[4367]: 5529068009D30: to=, relay=none, delay=0.06, delays=0.01/0/0.05/0, dsn=5.4.4, status=bounced (Host or domain name not found. Name service error for name=hostname type=A: Host not found)
Please try to configure Postfix to use IPv4, i.e. set inet_protocols = ipv4 in the Postfix configuration file. And make sure you are using a working resolver in /etc/resolv.conf file.
Hi,
Thanks for reply. I have checked inet_protocols = ipv4 correctly configure.
/etc/resolv.conf file:-
search abcd.com
nameserver 8.8.8.8
nameserver 8.8.4.4
Need to add any other entry in resolv.conf file ????
You can comment or remove search abcd.com in your resolv.conf file and try again.
Even though I have followed this article to the letter, for some reason my mail server only sends messages, but never receives them.
Most likely there is an issue with your DNS, make sure that your domain has a working MX records.
I have my MX domain set up, so much so that it worked when I created the accounts manually. However, for me to use postfixadmin, I changed the server settings, following the entire procedure given on this page.
When using Webmin to manage my server, I noticed this error in postfix:
connect to mail.mydomain.net[private/dovecot-lmtp]: No such file or directory
Remembering, I didn’t do anything different, I just followed the procedures provided on this page. I think it’s a simple problem to solve, but as the settings provided here are very different from what I used to do, I don’t know where to start.
If you can help me, I appreciate it!
If you have a VPS with us, we can help you investigate this further and solve the issue. If not, make sure that the LMTP protocol is included in your dovecot config file, you can run this command to verify:
dovecot -n | grep protocols
In my dovecot config file: /etc/dovecot/dovecot.conf
# Protocols we want to be serving.
protocols = imap pop3 lmtp
As you can see, lmtp is enabled along with imap and pop3. Is lmtp important? Couldn’t I just use imap instead?
We are sorry, but deeper investigation is required. You can check our sister company website at https://linuxhostsupport.com/ and ask for a quote there.