OVERVIEW DNS RECORDS DOCKER POSTFIX DOVECOT ROUNDCUBE

BUILD A SELF-HOSTED MAIL SERVER

// Own your inbox. Escape the surveillance economy.

// WHAT WE'RE BUILDING

In this project, you'll build a complete mail server from scratch using Postfix (SMTP), Dovecot (IMAP/POP3), and Roundcube (webmail). You'll have full control over your email - no Gmail, no Outlook, no tracking.

// WHY THIS MATTERS

Email is the backbone of digital communication, yet most people trust tech giants with their inbox. Self-hosted email means: no scanning of your emails for ads, no "sorry, you can't send that" restrictions, complete control over your data, and true privacy.

// What You'll Learn

This project combines multiple skills into a production-ready mail system:

How Email Works

Understanding the email architecture:

  1. MUA (Mail User Agent) - Your email client (Thunderbird, Roundcube, phone mail app)
  2. MTA (Mail Transfer Agent) - Postfix routes emails between servers (SMTP)
  3. MDA (Mail Delivery Agent) - Dovecot delivers mail to mailboxes (IMAP/POP3)
  4. MX Records - DNS tells the world which server accepts mail for your domain

Prerequisites

Before starting, make sure you've completed:

The Architecture

Your mail system will use these components:

// Part 1: Prerequisites & Domain Setup

Before installing mail software, we need to set up our domain and server.

1.1 Choose a Domain

You'll need a domain for email. Let's use example.com as our example. You'll need:

1.2 Set Hostname

$ sudo hostnamectl set-hostname mail.example.com
# Set the hostname
$ echo "127.0.1.1 mail.example.com mail" | sudo tee -a /etc/hosts
# Add to hosts file

1.3 Install Required Packages

$ sudo apt update
$ sudo apt install -y postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql mariadb-server nginx certbot python3-certbot-nginx
# Install mail stack

1.4 Create SSL Certificate

$ sudo certbot certonly --standalone -d mail.example.com
# Get SSL certificate

1.5 Create Database

$ sudo mysql -u root -p
# Connect to MariaDB
mysql> CREATE DATABASE mailserver;
mysql> CREATE USER 'mailuser'@'localhost' IDENTIFIED BY 'strong_password_here';
mysql> GRANT ALL PRIVILEGES ON mailserver.* TO 'mailuser'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> USE mailserver;

1.6 Create Mail Tables

mysql> CREATE TABLE virtual_domains (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

mysql> CREATE TABLE virtual_users (
    id INT NOT NULL AUTO_INCREMENT,
    domain_id INT NOT NULL,
    email VARCHAR(100) NOT NULL,
    password VARCHAR(150) NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY email (email),
    FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

mysql> CREATE TABLE virtual_aliases (
    id INT NOT NULL AUTO_INCREMENT,
    domain_id INT NOT NULL,
    source VARCHAR(100) NOT NULL,
    destination VARCHAR(100) NOT NULL,
    PRIMARY KEY (id),
    FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

1.7 Add Initial Data

mysql> INSERT INTO virtual_domains (name) VALUES ('example.com');
mysql> INSERT INTO virtual_users (domain_id, email, password) VALUES 
    (1, 'admin@example.com', ENCRYPT('your_secure_password'));
mysql> EXIT;

// Part 2: Configure Postfix

Postfix is the MTA (Mail Transfer Agent) that handles sending and receiving emails.

2.1 Backup Original Config

$ sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.backup
$ sudo cp /etc/postfix/master.cf /etc/postfix/master.cf.backup

2.2 Configure main.cf

$ sudo nano /etc/postfix/main.cf

Replace the file with this configuration:

# Host and domain settings
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = $myhostname, localhost, localhost.localdomain
mynetworks = 127.0.0.0/8
inet_interfaces = all
inet_protocols = all

# Mailbox settings
home_mailbox = Maildir/
mailbox_command =

# Virtual mail settings
virtual_alias_domains = 
virtual_alias_maps = proxy:mysql:/etc/postfix/mysql/virtual_alias_maps.cf
virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql/virtual_mailbox_domains.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql/virtual_mailbox_maps.cf
virtual_mailbox_base = /var/vmail
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

# SASL authentication
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options
smtpd_tls_auth_only = yes

# TLS/SSL settings
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_use_tls = yes

# Restrictions
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_rbl_client zen.spamhaus.org
smtpd_relay_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination

2.3 Create MySQL Config Files

$ sudo mkdir -p /etc/postfix/mysql
$ sudo nano /etc/postfix/mysql/virtual_mailbox_domains.cf
user = mailuser password = strong_password_here hosts = 127.0.0.1 dbname = mailserver query = SELECT name FROM virtual_domains WHERE name='%s'
$ sudo nano /etc/postfix/mysql/virtual_mailbox_maps.cf
user = mailuser password = strong_password_here hosts = 127.0.0.1 dbname = mailserver query = SELECT CONCAT(email,'@',virtual_domains.name) FROM virtual_users JOIN virtual_domains ON virtual_users.domain_id = virtual_domains.id WHERE email='%u' AND virtual_domains.name='%d'
$ sudo nano /etc/postfix/mysql/virtual_alias_maps.cf
user = mailuser password = strong_password_here hosts = 127.0.0.1 dbname = mailserver query = SELECT destination FROM virtual_aliases JOIN virtual_domains ON virtual_aliases.domain_id = virtual_domains.id WHERE source='%u@%d'

2.4 Create Mail User

$ sudo useradd -r -u 5000 -g mail -s /sbin/nologin -d /var/vmail vmail
$ sudo mkdir -p /var/vmail
$ sudo chown vmail:mail /var/vmail

2.5 Configure Master.cf

$ sudo nano /etc/postfix/master.cf

Make sure these lines exist (uncomment if needed):

submission inet n - - - - smtpd -o syslog_name=postfix/submission -o smtpd_sasl_auth_enable=yes -o smtpd_tls_auth_only=yes -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING

2.6 Restart Postfix

$ sudo postfix check
# Check for errors
$ sudo systemctl restart postfix

// Part 3: Configure Dovecot

Dovecot handles IMAP and POP3 - how you retrieve your emails.

3.1 Configure Main Dovecot Settings

$ sudo nano /etc/dovecot/dovecot.conf
!include conf.d/*.conf # Enable protocols protocols = imap pop3 lmtp # Listen on all interfaces listen = * # Disable SSLv3 (security) ssl_protocols = !SSLv3

3.2 Configure Authentication

$ sudo nano /etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = yes auth_mechanisms = plain login # Use MySQL for authentication !include auth-sql.conf.ext

3.3 Configure Mail Location

$ sudo nano /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:/var/vmail/%d/%n mail_uid = 5000 mail_gid = 5000 mail_privileged_group = mail

3.4 Configure SQL Connection

$ sudo nano /etc/dovecot/dovecot-sql.conf.ext
driver = mysql connect = host=127.0.0.1 dbname=mailserver user=mailuser password=strong_password_here default_pass_scheme = SHA512-CRYPT password_query = SELECT email AS user, password FROM virtual_users JOIN virtual_domains ON virtual_users.domain_id = virtual_domains.id WHERE email='%u'; user_query = SELECT 5000 AS uid, 5000 AS gid, '/var/vmail/%d/%n' AS home FROM virtual_users JOIN virtual_domains ON virtual_users.domain_id = virtual_domains.id WHERE email='%u';

3.5 Configure SSL

$ sudo nano /etc/dovecot/conf.d/10-ssl.conf
ssl = required ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem

3.6 Configure SASL in Postfix

$ sudo nano /etc/dovecot/conf.d/10-master.conf
service imap-login { inet_listener imap { port = 0 } inet_listener imaps { port = 993 ssl = yes } } service pop3-login { inet_listener pop3 { port = 0 } inet_listener pop3s { port = 995 ssl = yes } } service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600 user = postfix group = postfix } } service auth { unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix } }

3.7 Restart Dovecot

$ sudo systemctl restart dovecot

3.8 Test IMAP Connection

$ openssl s_client -connect mail.example.com:993
# Test SSL connection

// Part 4: Install Roundcube Webmail

Roundcube gives you a beautiful web interface to check your email.

4.1 Install Roundcube

$ sudo apt install roundcube roundcube-mysql roundcube-php

4.2 Configure Nginx for Roundcube

$ sudo nano /etc/nginx/sites-available/mail.example.com
server { listen 80; server_name mail.example.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name mail.example.com; ssl_certificate /etc/letsencrypt/live/mail.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mail.example.com/privkey.pem; root /var/lib/roundcube; index index.php index.html; location / { try_files $uri $uri/ /index.php; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.ht { deny all; } }

4.3 Enable the Site

$ sudo ln -s /etc/nginx/sites-available/mail.example.com /etc/nginx/sites-enabled/
$ sudo nginx -t
$ sudo systemctl reload nginx

4.4 Configure Roundcube Database

$ sudo mysql -u root -p < /usr/share/roundcube/SQL/mysql.initial.sql

4.5 Configure Roundcube

$ sudo nano /var/lib/roundcube/config/config.inc.php
<?php $config['db_dsnw'] = 'mysql://roundcube:roundcube_password@localhost/roundcube'; $config['default_host'] = 'ssl://mail.example.com'; $config['default_port'] = 993; $config['smtp_server'] = 'ssl://mail.example.com'; $config['smtp_port'] = 465; $config['smtp_user'] = '%u'; $config['smtp_pass'] = '%p'; $config['support_url'] = 'https://example.com'; $config['product_name'] = 'My Mail Server'; $config['des_key'] = 'your-rcryption-key-here';

Important: Generate a random des_key using: openssl rand -base64 24

// Part 5: Email Security (SPF, DKIM, DMARC)

These DNS records prove your server is authorized to send email for your domain.

5.1 What Are SPF, DKIM, DMARC?

5.2 Configure SPF

Add this TXT record to your domain's DNS:

TXT @ v=spf1 mx a:mail.example.com ~all

5.3 Install OpenDKIM

$ sudo apt install opendkim opendkim-tools

5.4 Configure OpenDKIM

$ sudo nano /etc/opendkim.conf
AutoRestart Yes AutoRestartRate 10/1M KeyTable /etc/opendkim/key.table SigningTable refile:/etc/opendkim/signing.table ExternalIgnoreList refile:/etc/opendkim/trusted.hosts InternalHosts refile:/etc/opendkim/trusted.hosts Syslog Yes SyslogSuccess Yes LogWhy Yes KeyFile /etc/opendkim/keys/default.private Selector default Socket inet:8891@127.0.0.1

5.5 Create Signing Keys

$ sudo mkdir -p /etc/opendkim/keys
$ sudo opendkim-genkey -s default -d example.com -D /etc/opendkim/keys
$ sudo chown -R opendkim:opendkim /etc/opendkim/keys

5.6 Configure Signing Table

$ sudo nano /etc/opendkim/signing.table
*@example.com default._domainkey.example.com

5.7 Configure Key Table

$ sudo nano /etc/opendkim/key.table
default._domainkey.example.com example.com:default:/etc/opendkim/keys/default.private

5.8 Configure Trusted Hosts

$ sudo nano /etc/opendkim/trusted.hosts
127.0.0.1 ::1 *.example.com

5.9 Connect Postfix to OpenDKIM

$ sudo nano /etc/postfix/main.cf
milter_default_action = accept milter_protocol = 6 smtpd_milters = inet:127.0.0.1:8891 non_smtpd_milters = inet:127.0.0.1:8891

5.10 Restart Services

$ sudo systemctl restart opendkim postfix

5.11 Add DKIM DNS Record

Get your public key:

$ sudo cat /etc/opendkim/keys/default.txt

Add this TXT record to your DNS:

TXT default._domainkey v=DKIM1; k=rsa; p=YOUR_PUBLIC_KEY_HERE

5.12 Configure DMARC

Add this TXT record to your DNS:

TXT _dmarc v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com

// Part 6: DNS Configuration

Your domain's DNS tells the world how to deliver email to your server.

6.1 MX Record

Add this record to point mail to your server:

MX @ mail.example.com priority 10

6.2 A Record for Mail Server

A mail Your_Server_IP_Address

6.3 Reverse DNS (PTR)

Set your server's reverse DNS to mail.example.com. This must be done through your hosting provider.

6.4 Test Your Setup

$ dig MX example.com
# Check MX record
$ dig TXT example.com
# Check SPF record
$ dig TXT default._domainkey.example.com
# Check DKIM record
$ dig TXT _dmarc.example.com
# Check DMARC record
$ mail-tester.com
# Test your email deliverability

// Part 7: Testing Your Mail Server

7.1 Test SMTP Connection

$ openssl s_client -connect mail.example.com:465 -starttls smtp

7.2 Test Sending Email

$ swaks --to test@example.com --from admin@example.com --server mail.example.com --port 587 --tls --auth-user admin@example.com --auth-password your_password

7.3 Access Webmail

Open in your browser:

https://mail.example.com

Login with:

7.4 Configure Email Client

Connect your desktop or mobile email client:

// What's Next?

Congratulations! You now have a fully functional mail server. Here's what you can add:

Remember: Running a mail server requires ongoing maintenance. Monitor your logs, keep your system updated, and watch your sender reputation.