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 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 nessesary 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
[email protected]${DOMAIN}
# 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 
-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 < /etc/opendkim/TrustedHosts < /etc/opendkim/SigningTable < /etc/opendkim/KeyTable <

Configure Postfix

# Configure postfix
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"
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
sed -i '/^#smtpd.*/ s/^#//' /etc/postfix/

cat <> /etc/postfix/
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}

cat > /etc/postfix/virtual_alias < /etc/postfix/virtual_domains <

Configure Dovecot

cat > /etc/dovecot/dovecot.conf <

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 protected]>
# sudo ./add_new_account <[email protected]> 

# 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 protected] 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.

Leave a Reply