Setting Up Your Own Mail Server on Ubuntu

on

It is far more professional to have an email address with your own domain name, e.g. firstname@surname.tld, rather than one offered by a free email provider. Sure, you could buy a custom email address from one of the several hosting companies out there, but how about setting up your own email server?

Whether you feel you’re spending too much for your custom email address, or you’re worried about your privacy, or just for the sake of it, it is worth considering having your own email server. You just need to either have an unused computer at home/office with a static IP address, or buy a cheap VPS from your favorite hosting provider.

In this tutorial, you will learn how to set up an email server on Ubuntu. The email server will be based on the following applications:

  • Postfix: A popular MTA (Mail Transfer Agent) used for sending and receiving emails.
  • Dovecot: An IMAP and POP3 server used by email clients, e.g. Mozilla Thunderbird, for transferring emails.
  • SQLite: A database system used for storing user mailbox accounts and other settings.

If you are planning to set up your mail server on a new VPS, I recommend that you read my post, Your First Steps with Your Brand New VPS Server, before following this tutorial. Also, you will need a SSL certificate for your mail server. If you don’t have one yet, check my post on how to obtain a free Let’s Encrypt certificate. You could also create your own custom self-signed certificate, which I don’t recommend if you’re new at this.

In the following configuration, it is assumed that your email server hostname is MAIL.DOMAIN.TLD and your email address will be USER@DOMAIN.TLD, so replace as appropriate.

The steps below have been tested on Ubuntu.

Postfix

Postfix is our Mail Transfer Agent (MTA) used for sending and receiving emails.

Install

apt install postfix postfix-sqlite

Before proceeding to configuring Postfix, create a new Unix user account for Postfix to use when writing to virtual mailbox files.

groupadd --system vmail -g 5000
useradd --system vmail -u 5000 -g 5000
mkdir /var/spool/mail/virtual
chown -R vmail:vmail /var/spool/mail/virtual && chmod 770 /var/spool/mail/virtual

And insert your mail server hostname in /etc/hosts.

nano /etc/hosts
YOUR_EXTERNAL_IP  MAIL.DOMAIN.TLD

And in /etc/mailname.

nano /etc/mailname
MAIL.DOMAIN.TLD

Modify main.cf

nano /etc/postfix/main.cf

Replace with the following contents:

# General
myhostname = MAIL.DOMAIN.TLD
myorigin = /etc/mailname
mydestination =
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
mynetworks_style = host
append_dot_mydomain = no
local_recipient_maps =
readme_directory = no
compatibility_level = 2

# SMTPD parameters
smtpd_banner = $myhostname ESMTP $mail_name
biff = no
delay_warning_time = 0h
maximal_queue_lifetime = 1d
bounce_queue_lifetime = 1d
minimal_backoff_time = 1000s
maximal_backoff_time = 8000s
smtp_helo_timeout = 60s
smtpd_recipient_limit = 20
smtpd_soft_error_limit = 3
smtpd_hard_error_limit = 12

smtpd_helo_required = yes
smtpd_delay_reject = yes
disable_vrfy_command = yes
strict_rfc821_envelopes = yes

smtpd_client_restrictions =
 permit_mynetworks
 permit_sasl_authenticated
 reject_unknown_client_hostname
 reject_rbl_client bl.spamcop.net
 reject_rbl_client zen.spamhaus.org
 reject_rbl_client blackholes.easynet.nl
smtpd_helo_restrictions =
 permit_mynetworks
 permit_sasl_authenticated
 reject_invalid_helo_hostname
 reject_non_fqdn_helo_hostname
smtpd_sender_restrictions =
 permit_mynetworks
 permit_sasl_authenticated
 reject_non_fqdn_sender
 reject_unknown_sender_domain
 reject_unauth_pipelining
smtpd_recipient_restrictions =
 reject_non_fqdn_recipient
 reject_unknown_recipient_domain
 reject_unauth_pipelining
 permit_mynetworks
 permit_sasl_authenticated
 reject_unauth_destination
smtpd_data_restrictions =
 reject_unauth_pipelining

# Virtual domains setup
alias_maps = hash:/etc/postfix/aliases
alias_database = hash:/etc/postfix/aliases
virtual_mailbox_base = /var/spool/mail/virtual
virtual_mailbox_maps = sqlite:/etc/postfix/sqlite_mailbox.cf
virtual_alias_maps = sqlite:/etc/postfix/sqlite_alias.cf
virtual_mailbox_domains = sqlite:/etc/postfix/sqlite_domains.cf
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

# SASL parameters
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = no
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
smtpd_sasl_authenticated_header = no

# TLS parameters
smtpd_tls_cert_file = /PATH/TO/SSL_CERTIFICATE
smtpd_tls_key_file = /PATH/TO/SSL_CERTIFICATE_KEY
smtpd_tls_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtp_tls_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtp_tls_ciphers = high
smtpd_tls_ciphers = high
smtpd_tls_mandatory_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtp_tls_mandatory_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtp_tls_mandatory_ciphers = high
smtpd_tls_mandatory_ciphers = high
smtpd_tls_security_level = may
smtp_tls_security_level = may
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

tls_random_source = dev:/dev/urandom
smtp_tls_note_starttls_offer = yes
smtpd_tls_auth_only = yes
smtpd_tls_loglevel = 0
smtpd_tls_received_header = yes

# Dovecot
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1

Make sure to use your own certificate paths in place of /PATH/TO/SSL_CERTIFICATE and /PATH/TO/SSL_CERTIFICATE_KEY.

Create Postfix aliases

cp /etc/aliases /etc/postfix/aliases
nano /etc/postfix/aliases
root: USER@DOMAIN.TLD
postmaster: USER@DOMAIN.TLD
postalias /etc/postfix/aliases

Create SQLite lookup files

mailbox

nano /etc/postfix/sqlite_mailbox.cf
dbpath = /etc/postfix/vmail.sqlite
query = SELECT email FROM users WHERE email = '%s'
result_format = %d/%u/

domains

nano /etc/postfix/sqlite_domains.cf
dbpath = /etc/postfix/vmail.sqlite
query = SELECT domain FROM domains WHERE domain = '%s'

aliases

nano /etc/postfix/sqlite_alias.cf
dbpath = /etc/postfix/vmail.sqlite
query = SELECT alias FROM aliases WHERE email = '%s'

Modify master.cf

nano /etc/postfix/master.cf
submission inet n - y - - smtpd
 -o syslog_name=postfix/submission
 -o smtpd_tls_security_level=encrypt
 -o smtpd_sasl_auth_enable=yes
 -o smtpd_tls_auth_only=yes
 -o smtpd_client_restrictions=permit_sasl_authenticated,reject
 -o smtpd_sasl_security_options=noanonymous,noplaintext
 -o smtpd_sasl_tls_security_options=noanonymous

dovecot unix - n n - - pipe
 flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d $(recipient)

Dovecot

Dovecot is our IMAP server, which is used by email clients for reading our emails.

Install

apt install dovecot-imapd dovecot-sqlite

Configure

auth-sql.conf.ext

nano /etc/dovecot/conf.d/auth-sql.conf.ext
passdb {
 driver = sql
 args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
 driver = sql
 args = /etc/dovecot/dovecot-sql.conf.ext
}

dovecot-sql.conf.ext

nano /etc/dovecot/dovecot-sql.conf.ext
driver = sqlite
connect = /etc/postfix/vmail.sqlite
default_pass_scheme = SSHA512
password_query = SELECT password FROM users WHERE email = '%u'
user_query = SELECT '/var/spool/mail/virtual/%d/%n' AS home, \
5000 AS uid, 5000 AS gid, '*:storage=' || quota AS quota_rule \
FROM users WHERE email = '%u'

auth.conf

nano /etc/dovecot/conf.d/10-auth.conf

First comment (#) all lines starting with !include and then add the section below:

disable_plaintext_auth = yes
auth_mechanisms = plain login
!include auth-sql.conf.ext

mail.conf

nano /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:/var/spool/mail/virtual/%d/%n/Maildir
mail_uid = vmail
mail_gid = vmail
first_valid_uid = 5000
last_valid_uid = 5000

ssl.conf

nano /etc/dovecot/conf.d/10-ssl.conf
ssl = required
ssl_cert = </PATH/TO/SSL_CERTIFICATE
ssl_key = </PATH/TO/SSL_CERTIFICATE_KEY

Make sure to replace /PATH/TO/SSL_CERTIFICATE and /PATH/TO/SSL_CERTIFICATE_KEY with your certificate paths.

master.conf

nano /etc/dovecot/conf.d/10-master.conf
service auth {
 unix_listener auth-userdb {
  mode = 0600
  user = vmail
  group = vmail
 }
 unix_listener /var/spool/postfix/private/auth {
  mode = 0660
  user = postfix
  group = postfix
 }
}

imap.conf

nano /etc/dovecot/conf.d/20-imap.conf
protocol imap {
 mail_plugins = quota imap_quota
}

quota.conf

nano /etc/dovecot/conf.d/90-quota.conf
plugin {
 quota = maildir:User quota
}

lda.conf

nano /etc/dovecot/conf.d/15-lda.conf
postmaster_address = postmaster@DOMAIN.TLD

Set permissions

chown -R vmail:dovecot /etc/dovecot && chmod -R o-rwx /etc/dovecot

SQLite

SQLite is our database used for storing our mailbox accounts and other settings.

Install

apt install sqlite3

Create a new SQLite database

sqlite3 /etc/postfix/vmail.sqlite

The sqlite3 command allows you to manually execute SQL statements against the specified SQLite database. If the database does not exist, it will be created.

  1. First, create tables for the users that will have a mailbox account, the domains that the mail server will handle, and the email aliases. Copy-paste the following commands in the SQLite shell, and press Enter:
    CREATE TABLE users (email TEXT PRIMARY KEY, password TEXT, quota INTEGER DEFAULT 0);
    CREATE TABLE domains (id INTEGER PRIMARY KEY, domain TEXT UNIQUE);
    CREATE TABLE aliases (id INTEGER PRIMARY KEY, email TEXT UNIQUE, alias TEXT);
    
  2. Assuming that your registered domain is DOMAIN.TLD, insert a new row in the domains table by copy-pasting the command below, and press Enter:
    INSERT INTO domains (domain) VALUES ('DOMAIN.TLD');
    
  3. Each user must have a password. It is always a good practice to hash passwords stored in a database, in case the database is compromised. If a user’s password is MY_PASSWORD, you can hash it using Dovecot’s password hash generator:
    doveadm pw -s SSHA512
    Enter new password:MY_PASSWORD
    Retype new password:MY_PASSWORD
    {SSHA512}HASHED_PASSWORD
    
  4. Assuming that you want to create a new mailbox account with email address USER@DOMAIN.TLD and a 2000000 KB quota, you should insert a new row in the users table as shown below. The user’s password must be hashed, so make sure to replace HASHED_PASSWORD with the exact output of the command in Step 3:
    INSERT INTO users (email,password,quota) VALUES ('USER@DOMAIN.TLD','HASHED_PASSWORD', 2000000);
    
  5. You can also create aliases to forward messages from an email address to a mailbox account. The postmaster@DOMAIN.TLD alias is required for all email servers and it’s also good to have an abuse@DOMAIN.TLD alias:
    INSERT INTO aliases (email,alias) VALUES ('postmaster@DOMAIN.TLD','USER@DOMAIN.TLD');
    INSERT INTO aliases (email,alias) VALUES ('abuse@DOMAIN.TLD','USER@DOMAIN.TLD');
    

Press Ctrl+D to exit the SQLite shell, and then change your database permissions so that it is only accessible by root.

chown root:root /etc/postfix/vmail.sqlite && chmod 600 /etc/postfix/vmail.sqlite

UFW (optional)

If you have followed my Your First Steps with Your Brand New VPS Server guide, you will probably need to open your SMTP and IMAP ports before continuing.

ufw allow 25
ufw allow 993
ufw allow 587

Restart Daemons

You should restart Postfix and Dovecot for changes to take effect.

service postfix restart
service dovecot restart

Mozilla Thunderbird

You should now be able to read and send emails from your server using an email client, such as Thunderbird. Create a new mail account using the settings below:

Incoming server

Server type: IMAP
Server name: MAIL.DOMAIN.TLD
Port: 993
User name: USER@DOMAIN.TLD
Connection security: SSL/TLS
Authentication method: Normal password

Outgoing server

Server name: MAIL.DOMAIN.TLD
Port: 587
User name: USER@DOMAIN.TLD
Connection security: STARTTLS
Authentication method: Normal password

In case of troubleshooting, you should check the log file at /var/log/mail.log to see if any errors have been reported by Postfix/Dovecot.

SPF (Sender Policy Framework) allows the recipients of your email to verify that your email server is authorized to send emails for your domain.

Enabling SPF for a domain is very easy, just attach a DNS TXT record on your entire domain (DOMAIN.TLD) with the value below. You should leave the TXT name field empty.

v=spf1 mx -all

The above line authorizes all servers mentioned in your DNS MX record to send emails for that domain.

DKIM (DomainKeys Identified Mail) is a method for signing emails with a digital signature, which guarantees to the recipients of your email that the email was indeed sent by you and the contents have not been modified by a third party.

apt install opendkim opendkim-tools

Modify OpenDKIM configuration:

nano /etc/opendkim.conf
KeyTable           /etc/opendkim/key_table
SigningTable       /etc/opendkim/signing_table
ExternalIgnoreList /etc/opendkim/trusted_hosts
InternalHosts      /etc/opendkim/trusted_hosts
AutoRestart             Yes
AutoRestartRate         10/1h
Mode                    sv
SignatureAlgorithm      rsa-sha256
Canonicalization        relaxed/simple
UserID                  opendkim:opendkim
Socket                  inet:8891@localhost
nano /etc/default/opendkim
SOCKET=inet:8891@localhost
mkdir /etc/opendkim
nano /etc/opendkim/trusted_hosts
127.0.0.1

Generate a key pair for DOMAIN.TLD:

mkdir /etc/opendkim/DOMAIN.TLD
cd /etc/opendkim/DOMAIN.TLD
opendkim-genkey -s mail -d DOMAIN.TLD
chown opendkim:opendkim mail.private
nano /etc/opendkim/key_table
mail._domainkey.DOMAIN.TLD DOMAIN.TLD:mail:/etc/opendkim/DOMAIN.TLD/mail.private
nano /etc/opendkim/signing_table
DOMAIN.TLD mail._domainkey.DOMAIN.TLD

The public key for DOMAIN.TLD will be in /etc/opendkim/DOMAIN.TLD/mail.txt. You’ll have to create a new DNS TXT record named mail._domainkey and copy-paste its contents (the part inside the parenthesis, including the quotes) in that record’s value.

Modify main.cf:

nano /etc/postfix/main.cf
# DKIM
milter_protocol = 6
milter_default_action = accept
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

Make sure to replace DOMAIN.TLD with the actual domain from which your emails are originating.

service opendkim restart
service postfix restart

That’s it! You are now a postmaster!

If you feel adventurous and you want to further customize your mail server, you can have a look at the Postfix and Dovecot documentations.