Setting Up Your Own Mail Server on Ubuntu
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.
- 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 (recommended)
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 (recommended)
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.