Categories ScriptsTutorials

How to setup a mail server with virtual domains using Postfix, Dovecot and OpenDKIM

In this article we will walk you through the installation and configuration of a mail server with virtual domains and users using Postfix, Dovecot and OpenDKIM on an Ubuntu VPS. By using virtual domains and users, you can set up unlimited email accounts without creating system users. This should work on other Linux VPS systems as well but was tested and written for Ubuntu.

Set the Hostname & FQDN

To check the existing FQDN, run the following command

hostname -f

which should display something like localhost or host.domain.com If you see localhost, proceed with the following steps:

  • Add your FQDN to the /etc/hosts file
  • Add your FQDN to the /etc/hostname file and run
    service hostname start

Install all the necessary packages

debconf-set-selections <<< "postfix postfix/mailname string $(hostname -f)"
debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Internet Site'"
apt-get update
apt-get install -y procmail postfix dovecot-core dovecot-pop3d dovecot-imapd opendkim opendkim-tools

Create a Virtual Email user

groupadd -g 5000 vmail
useradd -u 5000 -g vmail -s /sbin/nologin -d /home/vmail -m vmail
usermod -aG vmail postfix
usermod -aG vmail dovecot

mkdir -p /var/mail/vhosts/${DOMAIN}
chown -R vmail:vmail /var/mail/vhosts
chmod -R 775 /var/mail/vhosts

Generate SSL Certificate


# Certificate details; replace items in angle brackets with your own info
ST=New York
# Empty passphrase

# Makedir for certs
mkdir -p ${SSL_DIR}

# Generate the server private key
openssl genrsa -out "${SSL_DIR}/${DOMAIN}.key" 2048

# Generate the CSR
openssl req \
-new \
-batch \
-subj "$(echo -n "$SUBJ" | tr "\n" "/")" \
-key "${SSL_DIR}/${DOMAIN}.key" \
-out "${SSL_DIR}/${DOMAIN}.csr" \
-passin env:PASSPHRASE

# Generate the cert (good for 10 years)
openssl x509 -req -days 3650 -in "${SSL_DIR}/${DOMAIN}.csr" -signkey "${SSL_DIR}/${DOMAIN}.key" -out "${SSL_DIR}/${DOMAIN}.crt"

Configure OpenDKIM


MY_IP=$(ip route get | awk '/ {print $NF}')

mkdir -p ${OPENDKIM_DIR}/${DOMAIN}
opendkim-genkey -r -s default -d ${DOMAIN} -D ${OPENDKIM_DIR}/${DOMAIN}

cat > /etc/opendkim.conf <<EOF
AutoRestart             Yes
UMask                   002
Syslog                  yes
AutoRestartRate         10/1h
Canonicalization        relaxed/simple
ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts
KeyTable                refile:/etc/opendkim/KeyTable
LogWhy                  Yes
Mode                    sv
PidFile                 /var/run/opendkim/opendkim.pid
SignatureAlgorithm      rsa-sha256
SigningTable            refile:/etc/opendkim/SigningTable
Socket                  inet:8891@localhost
SyslogSuccess           Yes
TemporaryDirectory      /var/tmp
UserID                  opendkim:opendkim

cat > /etc/opendkim/TrustedHosts <<EOF

cat > /etc/opendkim/SigningTable <<EOF
*@${DOMAIN} default._domainkey.${DOMAIN}

cat > /etc/opendkim/KeyTable <<EOF
default._domainkey.${DOMAIN} ${DOMAIN}:default:/etc/opendkim/keys/${DOMAIN}/default.private

chown -R opendkim: /etc/opendkim

Configure Postfix

# Configure postfix main.cf
postconf -e "smtpd_banner = \$myhostname ESMTP"
postconf -e "mydomain = ${DOMAIN}"
postconf -e "myorigin = \$mydomain"
postconf -e "mydestination = localhost, localhost.localdomain, $(hostname -f)"
postconf -e "virtual_mailbox_domains = /etc/postfix/virtual_domains"
postconf -e "virtual_mailbox_base = /var/mail/vhosts"
postconf -e "virtual_mailbox_maps = hash:/etc/postfix/vmailbox"
postconf -e "virtual_alias_maps = hash:/etc/postfix/virtual_alias"
postconf -e "virtual_minimum_uid = 100"
postconf -e "virtual_uid_maps = static:5000"
postconf -e "virtual_gid_maps = static:5000"
postconf -e "virtual_transport = virtual"
postconf -e "dovecot_destination_recipient_limit = 1"
postconf -e "smtpd_sasl_auth_enable = yes"
postconf -e "smtpd_sasl_type = dovecot"
postconf -e "smtpd_sasl_path = private/auth"
postconf -e "smtpd_sasl_security_options = noanonymous"
postconf -e "smtpd_sasl_local_domain = \$mydomain"
postconf -e "broken_sasl_auth_clients = yes"
postconf -e "smtpd_tls_security_level = may"
postconf -e "smtpd_tls_auth_only = no"
postconf -e "smtpd_tls_cert_file=${SSL_DIR}/${DOMAIN}.crt"
postconf -e "smtpd_tls_key_file=${SSL_DIR}/${DOMAIN}.key"
postconf -e "smtpd_tls_received_header = yes"
postconf -e "tls_random_source = dev:/dev/urandom"
postconf -e "smtpd_tls_security_level = may"
postconf -e "smtp_tls_security_level = may"
postconf -e "smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_invalid_hostname, reject_unknown_client, reject_rbl_client sbl-xbl.spamhaus.org"
postconf -e "smtpd_sender_restrictions = permit_mynetworks, reject_unknown_address, reject_unknown_sender_domain, reject_non_fqdn_sender"
postconf -e "smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination"
postconf -e "smtpd_recipient_limit = 250"
postconf -e "milter_default_action = accept"
postconf -e "milter_protocol = 6"
postconf -e "smtpd_milters =  inet:"
postconf -e "non_smtpd_milters =  inet:"

# Configure postfix master.cf
sed -i '/^#smtpd.*/ s/^#//' /etc/postfix/master.cf

vi /etc/postfix/master.cf
# Add the following lines at the end:
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}

cat > /etc/postfix/virtual_alias <<EOF
# Example
# @${DOMAIN}          admin@${DOMAIN}
postmap /etc/postfix/virtual_alias

cat > /etc/postfix/virtual_domains <<EOF
postmap /etc/postfix/virtual_domains

touch /etc/postfix/vmailbox
postmap /etc/postfix/vmailbox

Configure Dovecot

cat > /etc/dovecot/dovecot.conf <<EOF
auth_mechanisms = plain login
disable_plaintext_auth = no
log_path = /var/log/dovecot
mail_location = maildir:/var/mail/vhosts/%d/%n
passdb {
  args = /var/mail/vhosts/%d/shadow
  driver = passwd-file
protocols = imap pop3
service auth {
  unix_listener /var/spool/postfix/private/auth {
    group = vmail
    mode = 0660
    user = postfix
  unix_listener auth-master {
    group = vmail
    mode = 0600
    user = vmail
ssl_cert = <${SSL_DIR}/${DOMAIN}.crt
ssl_key = <${SSL_DIR}/${DOMAIN}.key
userdb {
  args = /var/mail/vhosts/%d/passwd
  driver = passwd-file
protocol lda {
  auth_socket_path = /var/run/dovecot/auth-master
  hostname = $(hostname -f)
  mail_plugin_dir = /usr/lib/dovecot/modules
  mail_plugins = sieve
  postmaster_address = postmaster@${DOMAIN}

touch /var/log/dovecot
chgrp vmail /var/log/dovecot
chmod 660 /var/log/dovecot

Restart all services

service dovecot restart
service postfix restart
service opendkim restart

That’s it! If you followed all steps correctly, your email server should be fully functional.

Adding new email accounts

You can use the following script to add a new email account.  Save the script as ‘add_new_account’, use chmod to make it executable and then run it passing two parameters:

./add_new_account <email@account> <password>
# sudo ./add_new_account <email@account> <password>

# Functions
ok() { echo -e '\e[32m'${1}'\e[m'; } # Green
die() { echo -e '\e[1;31m'${1}'\e[m'; exit 1; } # Red

# Sanity check
[ $(id -g) != "0" ] && die "Script must be run as root."
[ $# != "2" ] && die "Usage: $(basename $0) email@account password"
[ ! -f /usr/sbin/postfix ] && die "Postfix is not installed"
[ ! -f /usr/bin/doveadm ] && die "Dovecot is not installed"

# Variables

# Get the Mailbox Base Directory
BASEDIR=$(postconf virtual_mailbox_base)

# Get the Mailbox Maps File
MAPSFILE=$(postconf virtual_mailbox_maps)

# Check if account exist
if grep -wq "^${ADDRESS}" ${MAPSFILE}
  die "The mail address ${ADDRESS} already exist"

if [[ ( -f ${MAPSFILE} && -d ${BASEDIR} ) ]]; then

  postmap ${MAPSFILE}
  if [[ $? -eq 0 ]]; then
    echo ${ADDRESS}":"$(doveadm pw -p $PASSWD) >> ${BASEDIR}/${DOMAIN}/shadow
    chown ${VMAIL}: $BASEDIR/${DOMAIN}/{passwd,shadow}
    chmod 775 ${BASEDIR}/${DOMAIN}/{passwd,shadow}
    service postfix reload
    ok "The email account has been addedd"
    die "Mailbox maps file or mailbox base directory doesn't exist"

Of course you don’t have to do any of this if you use one of our Linux VPS 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 on the left or simply leave a reply below. Thanks.

View Comments

  • Following your instructions, I'm not able to login to the smtp using Thunderbird. Also postfix sends emails via root@DOMAIN instead of the created user. Please help.

    • Please follow the instructions closely. Make sure you change ${DOMAIN} with your actual domain name.

    • Make sure the mail_location and directory permissions are correct. You should also check the log files for error messages.

  • Should/can the 'group = postfix' rather than 'vmail' under 'unix_listener /var/spool/postfix/private/auth {'. Does 'service auth {' section require 'user = dovecot' or 'root'? If possible, it would be helpful to know why the following are required and at what point each helps in the mail receipt or delivery process: usermod -aG vmail postfix usermod -aG vmail dovecot Appreciate the post.

    • Postfix and Dovecot are added to the vmail group because it is essential for the email server to work due to the vmail ownership of the files responsible for email handling.