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 successfully tested on Ubuntu 18.04.
Postfix
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
and /etc/mailname
.
$ nano /etc/hosts
YOUR_EXTERNAL_IP MAIL.DOMAIN.TLD
$ nano /etc/mailname
MAIL.DOMAIN.TLD
Edit main.cf
$ nano /etc/postfix/main.cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
# 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
Replace /PATH/TO/SSL_CERTIFICATE
and /PATH/TO/SSL_CERTIFICATE_KEY
with your certificate paths.
Edit Postfix aliases
$ cp /etc/aliases /etc/postfix/aliases
$ nano /etc/postfix/aliases
root: USER@DOMAIN.TLD
postmaster: USER@DOMAIN.TLD
$ postalias /etc/postfix/aliases
Edit SQLite lookup files
$ nano /etc/postfix/sqlite_mailbox.cf
dbpath = /etc/postfix/vmail.sqlite
query = SELECT email FROM users WHERE email = '%s'
result_format = %d/%u/
$ nano /etc/postfix/sqlite_domains.cf
dbpath = /etc/postfix/vmail.sqlite
query = SELECT domain FROM domains WHERE domain = '%s'
$ nano /etc/postfix/sqlite_alias.cf
dbpath = /etc/postfix/vmail.sqlite
query = SELECT alias FROM aliases WHERE email = '%s'
Edit 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
Install
$ apt install dovecot-imapd dovecot-sqlite
Edit Dovecot configuration
$ 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
}
$ 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'
$ nano /etc/dovecot/conf.d/10-auth.conf
# Comment (#) all lines above starting with !include
disable_plaintext_auth = yes
auth_mechanisms = plain login
!include auth-sql.conf.ext
$ 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
$ nano /etc/dovecot/conf.d/10-ssl.conf
ssl = required
ssl_cert = </PATH/TO/SSL_CERTIFICATE
ssl_key = </PATH/TO/SSL_CERTIFICATE_KEY
$ 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
}
}
$ nano /etc/dovecot/conf.d/20-imap.conf
protocol imap {
mail_plugins = quota imap_quota
}
$ nano /etc/dovecot/conf.d/90-quota.conf
plugin {
quota = maildir:User quota
}
$ nano /etc/dovecot/conf.d/15-lda.conf
postmaster_address = postmaster@DOMAIN.TLD
$ chown -R vmail:dovecot /etc/dovecot && chmod -R o-rwx /etc/dovecot
Replace /PATH/TO/SSL_CERTIFICATE
and /PATH/TO/SSL_CERTIFICATE_KEY
with your certificate paths.
SQLite
Install
$ apt install sqlite3
Open 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.
- First, create tables for the
users
that will have a mailbox account, thedomains
that the mail server will handle, and the emailaliases
. 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);
- Assuming that your registered domain is
DOMAIN.TLD
, insert a new row in thedomains
table by copy-pasting the command below, and press Enter:INSERT INTO domains (domain) VALUES ('DOMAIN.TLD');
- 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
- Assuming that you want to create a new mailbox account with email address
USER@DOMAIN.TLD
and a2000000
KB quota, you should insert a new row in theusers
table as shown below. The user's password must be hashed, so make sure to replaceHASHED_PASSWORD
with the exact output of the command in Step 3:INSERT INTO users (email,password,quota) VALUES ('USER@DOMAIN.TLD','HASHED_PASSWORD', 2000000);
- 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 anabuse@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 (optional)
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 (optional)
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
Edit 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.
Edit 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.
(Original publish date: September 10, 2015)