← Back to posts
comparison guide self-hosted · · 17 min read

Build Your Own Email Server: Postfix, Dovecot & Rspamd Complete Guide 2026

Complete guide to building a self-hosted email server from scratch using Postfix, Dovecot, and Rspamd — including Docker setup, TLS, SPF, DKIM, DMARC, and deliverability best practices.

OS
Editorial Team

Running your own email server is one of the most rewarding self-hosted projects you can undertake. It gives you complete control over your communications, eliminates third-party data harvesting, and teaches you a tremendous amount about how the internet actually works. In this guide, we will walk through building a production-ready mail server using the gold-standard open-source stack: Postfix for SMTP, Dovecot for IMAP/LMTP, and Rspamd for spam filtering — all orchestrated with docker Compose.

Why Self-Host Your Own Email

The case for self-hosting email has never been stronger. When you use Gmail, Outlook, or any hosted provider, every single message you send and receive passes through servers owned by corporations whose business models depend on analyzing your data. Even providers that claim not to scan content still hold the encryption keys and can comply with data requests without your knowledge.

Self-hosting solves this at the root level:

  • Complete data ownership — your messages never touch servers you do not control
  • No storage limits — your only constraint is your own disk space
  • Custom domains for free — no per-user pricing or subscription tiers
  • Deep privacy — combine with full-disk encryption and you have a fortress
  • Learning value — understanding SMTP, IMAP, DNS records, and mail routing is genuinely useful knowledge

The main challenge with self-hosted email is deliverability — getting your messages past other providers’ spam filters. We will address this head-on with proper DNS configuration, reputation building, and authentication records.

Architecture Overview

Our stack consists of four interconnected components:

ComponentRolePort
PostfixSMTP server — sends and receives mail25, 587, 465
DovecotIMAP/LMTP server — mailbox access and local delivery143, 993
RspamdSpam filter — content analysis, greylisting, DKIM snginxg11332-11334
Nginx (optional)Webmail front-end (Roundcube/Snappymail)80, 443

Here is how a typical message flows through the system:

  1. Incoming mail arrives at Postfix on port 25
  2. Postfix passes the message to Rspamd for spam scoring via milter
  3. Rspamd runs DNSBL checks, SPF/DKIM/DMARC verification, content analysis
  4. Clean messages are delivered via LMTP to Dovecot
  5. Dovecot stores messages in Maildir format and serves them via IMAP
  6. For outgoing mail, Postfix handles DKIM signing via Rspamd and relays to the recipient

Prerequisites

Before you begin, you need:

  • A VPS or dedicated server with a clean IP address (check against Spamhaus, Barracuda, and SpamCop blacklists)
  • A domain name with full DNS control
  • Port 25 open — many cloud providers (AWS, GCP, Vultr) block it by default; you may need to request unblocking or use a provider like Hetzner, OVH, or DigitalOcean
  • At least 1 GB RAM (2 GB recommended for Rspamd + ClamAV)
  • Reverse DNS (PTR record) configured by your hosting provider

Verify Your IP Is Clean

1
2
3
4
5
6
# Check against major DNSBLs
dig +short 1.2.3.4.zen.spamhaus.org
# Should return NXDOMAIN (no matches) if your IP is clean

# Check with a multi-RBL service
curl -s https://check.spamhaus.org/ | head -20

DNS Configuration

Proper DNS records are the single most important factor in email deliverability. Set these up before configuring any software.

Essential Records

Create the following records in your domain’s DNS zone:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# A record — points your mail hostname to your server IP
mail.example.com.    IN  A       203.0.113.50

# PTR record — reverse DNS (set in your hosting provider's control panel)
203.0.113.50  IN  PTR  mail.example.com.

# MX record — tells the world where to deliver email for your domain
example.com.         IN  MX  10  mail.example.com.

# SPF record — authorizes your server to send mail for your domain
example.com.         IN  TXT  "v=spf1 mx ip4:203.0.113.50 -all"

# DKIM record — public key for message signing (generated later)
default._domainkey.example.com.  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqh..."

# DMARC record — policy for handling authentication failures
_dmarc.example.com.  IN  TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com; ruf=mailto:dmarc@example.com; fo=1"

# Autoconfig records — helps email clients find settings automatically
autoconfig.example.com.  IN  CNAME  mail.example.com.

The -all in your SPF record means hard fail — any server not listed is unauthorized to send mail for your domain. Start with ~all (soft fail) during testing, then switch to -all once everything is working.

Understanding DMARC Policies

PolicyEffectRecommended Stage
p=noneMonitor only — no action on failuresInitial testing (1-2 weeks)
p=quarantineSend failing messages to spamAfter confirming SPF/DKIM pass
p=rejectReject failing messages outrightProduction — final configuration

Docker Compose Setup

We will use Docker Compose for clean service isolation and easy management.

Directory Structure

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/etc/mailserver/
├── docker-compose.yml
├── postfix/
│   ├── main.cf
│   └── master.cf
├── dovecot/
│   ├── dovecot.conf
│   └── conf.d/
├── rspamd/
│   ├── local.d/
│   │   ├── dkim_signing.conf
│   │   ├── dmarc.conf
│   │   └── options.inc
│   └── override.d/
├── data/
│   ├── mail/          # Maildir storage
│   └── vmail.db       # Virtual mailbox database (SQLite)
└── certs/
    ├── fullchain.pem  # TLS certificate
    └── privkey.pem    # TLS private key

docker-compose.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
version: "3.9"

services:
  postfix:
    image: postfix:latest
    build: ./postfix
    container_name: mail-postfix
    restart: unless-stopped
    ports:
      - "25:25"
      - "587:587"
      - "465:465"
    volumes:
      - ./postfix/main.cf:/etc/postfix/main.cf:ro
      - ./postfix/master.cf:/etc/postfix/master.cf:ro
      - ./data/mail:/var/mail/vhosts
      - ./certs:/etc/ssl/mail:ro
      - ./data/vmail.db:/etc/postfix/vmail.db
    environment:
      - HOSTNAME=mail.example.com
      - DOMAIN=example.com
    networks:
      - mailnet
    depends_on:
      - rspamd

  dovecot:
    image: dovecot:latest
    build: ./dovecot
    container_name: mail-dovecot
    restart: unless-stopped
    ports:
      - "143:143"
      - "993:993"
    volumes:
      - ./dovecot/dovecot.conf:/etc/dovecot/dovecot.conf:ro
      - ./dovecot/conf.d:/etc/dovecot/conf.d:ro
      - ./data/mail:/var/mail/vhosts
      - ./data/vmail.db:/etc/dovecot/users:ro
      - ./certs:/etc/ssl/mail:ro
    networks:
      - mailnet

  rspamd:
    image: rspamd/rspamd:latest
    container_name: mail-rspamd
    restart: unless-stopped
    expose:
      - "11332"
      - "11333"
    ports:
      - "11334:11334"  # Web UI (bind to localhost only)
    volumes:
      - ./rspamd/local.d:/etc/rspamd/local.d:ro
      - ./rspamd/override.d:/etc/rspamd/override.d
      - ./data/rspamd:/var/lib/rspamd
      - ./certs:/etc/ssl/mail:ro
    networks:
      - mailnet

networks:
  mailnet:
    driver: bridge

Postfix Configuration

The main.cf is the heart of your SMTP server. Here is a production-ready configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# Basic settings
smtpd_banner = $myhostname ESMTP
compatibility_level = 3.9
queue_directory = /var/spool/postfix
command_directory = /usr/sbin

# Identity
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = localhost
inet_interfaces = all
inet_protocols = ipv4

# Virtual mailbox setup
virtual_mailbox_domains = sqlite:/etc/postfix/vmail.db
virtual_mailbox_maps = sqlite:/etc/postfix/vmail.db
virtual_mailbox_base = /var/mail/vhosts
virtual_minimum_uid = 1000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_transport = lmtp:inet:dovecot:24

# TLS — mandatory for modern email
smtp_tls_security_level = may
smtpd_tls_security_level = may
smtp_tls_loglevel = 1
smtpd_tls_loglevel = 1
smtpd_tls_cert_file = /etc/ssl/mail/fullchain.pem
smtpd_tls_key_file = /etc/ssl/mail/privkey.pem
smtpd_tls_mandatory_protocols = >=TLSv1.2
smtpd_tls_mandatory_ciphers = high
smtpd_tls_received_header = yes
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

# SASL authentication via Dovecot
smtpd_sasl_type = dovecot
smtpd_sasl_path = inet:dovecot:12345
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = noanonymous

# Rspamd milter integration
smtpd_milters = inet:rspamd:11332
non_smtpd_milters = inet:rspamd:11332
milter_default_action = accept
milter_protocol = 6

# Restrictions — reject spam at the SMTP level
smtpd_sender_restrictions =
    reject_unknown_sender_domain,
    reject_non_fqdn_sender

smtpd_recipient_restrictions =
    permit_sasl_authenticated,
    permit_mynetworks,
    reject_unauth_destination,
    reject_non_fqdn_recipient,
    reject_unknown_recipient_domain

smtpd_relay_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination

# Rate limiting — prevent abuse
smtpd_client_connection_rate_limit = 50
smtpd_client_message_rate_limit = 100
anvil_rate_time_unit = 60s

# Mailbox size limit (0 = unlimited)
mailbox_size_limit = 0
virtual_mailbox_limit = 0

master.cf — Service Definitions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
# ==========================================================================
smtp      inet  n       -       y       -       -       smtpd
submission inet n      -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
smtps     inet  n       -       y       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
pickup    unix  n       -       y       60      1       pickup
cleanup   unix  n       -       y       -       0       cleanup
qmgr      unix  n       -       n       300     1       qmgr
tlsmgr    unix  -       -       y       1000?   1       tlsmgr
rewrite   unix  -       -       y       -       -       trivial-rewrite
bounce    unix  -       -       y       -       0       bounce
defer     unix  -       -       y       -       0       bounce
trace     unix  -       -       y       -       0       bounce
flush     unix  n       -       y       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
smtp      unix  -       -       y       -       -       smtp
relay     unix  -       -       y       -       -       smtp
showq     unix  n       -       y       -       -       showq
error     unix  -       -       y       -       -       error
retry     unix  -       -       y       -       -       error
discard   unix  -       -       y       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       y       -       -       lmtp
anvil     unix  -       -       y       -       1       anvil
scache    unix  -       -       y       -       1       scache
postlog   unix-dgram n  -       n       -       1       postlogd

Dovecot Configuration

Dovecot handles mailbox storage and IMAP access. The configuration below uses Maildir format with full-text search via Solr (optional).

dovecot.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# Protocols
protocols = imap lmtp

# Mail location — Maildir format
mail_location = maildir:/var/mail/vhosts/%d/%n/Maildir
mail_uid = 5000
mail_gid = 5000
first_valid_uid = 5000
last_valid_uid = 5000

# Namespace
namespace inbox {
    inbox = yes
    separator = /

    mailbox Drafts {
        special_use = \Drafts
        auto = subscribe
    }
    mailbox Sent {
        special_use = \Sent
        auto = subscribe
    }
    mailbox Trash {
        special_use = \Trash
        auto = subscribe
    }
    mailbox Spam {
        special_use = \Junk
        auto = subscribe
    }
    mailbox Archive {
        special_use = \Archive
        auto = subscribe
    }
}

# Authentication
auth_mechanisms = plain login
disable_plaintext_auth = yes

passdb {
    driver = passwd-file
    args = scheme=CRYPT username_format=%u /etc/dovecot/users
}

userdb {
    driver = passwd-file
    args = username_format=%u /etc/dovecot/users
    default_fields = uid=5000 gid=5000 home=/var/mail/vhosts/%d/%n
}

# SSL/TLS
ssl = required
ssl_cert = </etc/ssl/mail/fullchain.pem
ssl_key = </etc/ssl/mail/privkey.pem
ssl_min_protocol = TLSv1.2
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl_prefer_server_ciphers = yes

# LMTP service — receives mail from Postfix
service lmtp {
    inet_listener lmtp {
        address = 0.0.0.0
        port = 24
    }
}

# Auth service — provides SASL to Postfix
service auth {
    unix_listener /var/run/dovecot/auth-master {
        mode = 0600
        user = vmail
        group = vmail
    }
    inet_listener auth {
        address = 0.0.0.0
        port = 12345
    }
}

# Logging
log_path = /var/log/dovecot.log
info_log_path = /var/log/dovecot-info.log

# Mail plugins
mail_plugins = $mail_plugins quota
protocol imap {
    mail_plugins = $mail_plugins imap_quota imap_sieve
}

# Sieve for automatic spam filtering
plugin {
    sieve = file:~/sieve;active=~/.dovecot.sieve
    sieve_before = /etc/dovecot/sieve/before.sieve
    sieve_after = /etc/dovecot/sieve/after.sieve
}

User Database Format

Create /etc/dovecot/users (or vmail.db in our setup) with this format:

1
user@example.com:{CRYPT}$y$j9T$...:5000:5000::/var/mail/vhosts/example.com/user

Generate password hashes with:

1
2
3
doveadm pw -s BLF-CRYPT
# Enter password: ********
# Output: {BLF-CRYPT}$2y$05$...

Rspamd Configuration

Rspamd is a fast, modern spam filter that outperforms traditional SpamAssassin in both speed and accuracy.

local.d/options.inc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Bind address for milter
bind_socket = "*:11332";

# Worker configuration
worker "normal" {
    bind_socket = "*:11333";
    count = 1;
}

worker "controller" {
    bind_socket = "127.0.0.1:11334";
    password = "$2$your_hashed_password_here";
    secure_ip = "127.0.0.1";
}

worker "proxy" {
    bind_socket = "*:11332";
    milter = yes;
    timeout = 120s;
}

local.d/dkim_signing.conf

1
2
3
4
5
6
7
# Automatic DKIM signing for outgoing mail
domain {
    example.com {
        selector = default;
        path = "/var/lib/rspamd/dkim/example.com.default.key";
    }
}

local.d/dmarc.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# DMARC policy enforcement
actions {
    reject = "reject";
    quarantine = "add_header";
    greylist = "greylist";
}

reporting {
    email = "dmarc@example.com";
    domain = "example.com";
    org_name = "Example Mail Server";
}

Generating DKIM Keys

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Create DKIM directory
mkdir -p /etc/mailserver/data/rspamd/dkim

# Generate key pair
rspamadm dkim_keygen -d example.com -s default -k /etc/mailserver/data/rspamd/dkim/example.com.default.key > /etc/mailserver/data/rspamd/dkim/example.com.default.pub

# The .pub file contains the DNS TXT record value
cat /etc/mailserver/data/rspamd/dkim/example.com.default.pub
# Output: default._domainkey IN TXT ( "v=DKIM1; k=rsa; " "p=MIIBIjANBgkqhkiG9w0BAQE..." )

# Set correct permissions
chmod 640 /etc/mailserver/data/rspamd/dkim/*.key
chown 101:101 /etc/mailserver/data/rspamd/dkim/*.key

TLS Certificates with Let’s Encrypt

Use Certbot to obtain free TLS certificates:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# Install certbot
apt install certbot

# Obtain certificate (standalone mode — stop any web server first)
certbot certonly --standalone -d mail.example.com --email admin@example.com --agree-tos

# Copy certificates to mailserver directory
cp /etc/letsencrypt/live/mail.example.com/fullchain.pem /etc/mailserver/certs/
cp /etc/letsencrypt/live/mail.example.com/privkey.pem /etc/mailserver/certs/

# Set up auto-renewal hook
cat > /etc/letsencrypt/renewal-hooks/post/copy-mail-certs.sh << 'EOF'
#!/bin/bash
cp /etc/letsencrypt/live/mail.example.com/fullchain.pem /etc/mailserver/certs/
cp /etc/letsencrypt/live/mail.example.com/privkey.pem /etc/mailserver/certs/
docker compose -f /etc/mailserver/docker-compose.yml restart postfix dovecot
EOF
chmod +x /etc/letsencrypt/renewal-hooks/post/copy-mail-certs.sh

Launching the Server

With all configuration files in place:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cd /etc/mailserver
docker compose up -d

# Check all services are running
docker compose ps

# View logs
docker compose logs -f postfix
docker compose logs -f dovecot
docker compose logs -f rspamd

Testing and Verification

Send a Test Email

1
2
3
4
5
6
7
8
9
# Send a test message from the command line
echo "Subject: Test Email from Self-Hosted Server" | \
  sendmail -f user@example.com recipient@external.com

# Check the mail queue
docker compose exec postfix mailq

# Check delivery status
docker compose logs postfix | grep "status=sent"

Verify Authentication Records

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Check SPF
dig TXT example.com +short
# Should show: "v=spf1 mx ip4:203.0.113.50 -all"

# Check DKIM
dig TXT default._domainkey.example.com +short
# Should show the public key

# Check DMARC
dig TXT _dmarc.example.com +short
# Should show: "v=DMARC1; p=quarantine; ..."

# Verify MX
dig MX example.com +short
# Should show: 10 mail.example.com.

External Testing Services

Run these tests after sending your first emails:

  1. Mail-Tester.com — send an email to the address they provide and get a score out of 10
  2. MXToolbox — check blacklists, SMTP diagnostics, and DNS records
  3. DKIMValidator.com — paste raw email headers to verify DKIM/SPF/DMARC
  4. Google Postmaster Tools — monitor your domain reputation with Gmail

Spam Prevention and Deliverability

Getting your email into inboxes (not spam folders) is the hardest part of self-hosting. Here is a checklist:

Essential Deliverability Steps

  1. PTR Record — your IP’s reverse DNS must match your mail hostname
  2. SPF + DKIM + DMARC — all three must be configured and passing
  3. Warm Up Your IP — start by sending low volumes (10-20 emails/day) and gradually increase over 4-6 weeks
  4. Monitor Blacklists — check regularly at mxtoolbox.com/blacklists.aspx
  5. Set Up a Bounce Handler — process bounces promptly to maintain sender reputation
  6. Include an Unsubscribe Link — required by law in many jurisdictions for commercial email
  7. Use Double Opt-In — only send to users who explicitly confirmed their address

Rspamd Tuning Tips

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# local.d/multimap.conf — whitelist trusted senders
WHITELIST_SENDER_DOMAIN {
    type = "from";
    filter = "email:domain";
    map = "${LOCAL_CONFDIR}/local.d/whitelist_sender.domain.map";
    symbol = "WHITELIST_SENDER_DOMAIN";
    score = -5.0;
}

# local.d/composites.conf — reduce false positives
rule "MY_CUSTOM_RULE" {
    expression = "RBL_SPAMHAUS & FROM_NO_DNS";
    score = -2.0;
    description = "Known false positive pattern";
}

Handling Common Issues

SymptomLikely CauseFix
Emails land in Gmail spamMissing PTR or new IPVerify PTR, warm up IP, register with Google Postmaster
Rejected by Outlook/HotmailIP on blocklistCheck RBLs, request delisting
SPF failSending from different IPUpdate SPF record with all authorized IPs
DKIM failKey mismatch or wrong selectorVerify selector matches DNS record
DMARC failSPF or DKIM alignment issueCheck from domain matches SPF/DKIM domains

Backup Strategy

Email is irreplaceable — you need a robust backup plan:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/bash
# /etc/mailserver/backup.sh — daily mail backup

BACKUP_DIR="/backup/mail/$(date +%Y-%m-%d)"
MAIL_DIR="/etc/mailserver/data/mail"

mkdir -p "$BACKUP_DIR"

# Backup Maildir (incremental with rsync)
rsync -avz --delete "$MAIL_DIR/" "$BACKUP_DIR/mail/"

# Backup configuration
tar czf "$BACKUP_DIR/config.tar.gz" \
    /etc/mailserver/postfix/ \
    /etc/mailserver/dovecot/ \
    /etc/mailserver/rspamd/ \
    /etc/mailserver/docker-compose.yml

# Backup DKIM keys (critical — loss means all past emails fail verification)
tar czf "$BACKUP_DIR/dkim-keys.tar.gz" \
    /etc/mailserver/data/rspamd/dkim/

# Backup Rspamd data (bayes database, statistics)
tar czf "$BACKUP_DIR/rspamd-data.tar.gz" \
    /etc/mailserver/data/rspamd/

# Keep only 30 days of backups
find /backup/mail -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;

echo "Backup completed: $BACKUP_DIR"

Add to crontab:

1
0 3 * * * /etc/mailserver/backup.sh >> /var/log/mail-backup.log 2>&1

Monitoring

Set up basic health checks for your mail server:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
# /etc/mailserver/healthcheck.sh

# Check Postfix queue size
QUEUE_SIZE=$(docker compose exec postfix mailq | tail -1 | grep -oP '\d+' | head -1)
if [ "$QUEUE_SIZE" -gt 100 ]; then
    echo "WARNING: Mail queue has $QUEUE_SIZE messages"
fi

# Check disk space for mail storage
MAIL_USAGE=$(df -h /etc/mailserver/data/mail | tail -1 | awk '{print $5}')
if [ "${MAIL_USAGE%\%}" -gt 85 ]; then
    echo "WARNING: Mail storage is ${MAIL_USAGE} full"
fi

# Check if services are responding
docker compose ps --format json | jq -r '.[] | select(.State != "running") | .Name' | \
    while read -r service; do
        echo "CRITICAL: $service is not running"
    done

Migrating from Another Provider

To migrate your existing email to this server:

  1. Use imapsync to copy messages from your old provider:
1
2
3
imapsync --host1 old-provider.com --user1 you@old.com --password1 "oldpass" \
         --host2 mail.example.com --user2 you@example.com --password2 "newpass" \
         --ssl1 --ssl2 --automap --usecache
  1. Update MX records — lower TTL to 300 a few days before, then switch
  2. Keep old account active for 2-4 weeks to catch any straggling mail
  3. Update SPF to remove the old provider’s sending servers once fully migrated

Conclusion

Running your own email server is a commitment, but it is one of the most impactful self-hosting projects available. You gain complete control over your communications, learn valuable infrastructure skills, and free yourself from corporate email platforms.

The stack we covered — Postfix, Dovecot, and Rspamd — powers millions of mail servers worldwide. It is battle-tested, well-documented, and supported by massive communities. Combined with Docker Compose for easy deployment, Let’s Encrypt for free TLS, and proper DNS records for deliverability, you have a production-grade email system.

Key takeaways for success:

  • Test everything before switching your MX records
  • Warm up your IP gradually to build sender reputation
  • Monitor blacklists and respond quickly to any listing
  • Back up DKIM keys — losing them breaks verification for all past emails
  • Start with DMARC p=none and move to p=reject only after confirming everything works

Your email, your server, your rules.

Frequently Asked Questions (FAQ)

Which one should I choose in 2026?

The best choice depends on your specific requirements:

  • For beginners: Start with the simplest option that covers your core use case
  • For production: Choose the solution with the most active community and documentation
  • For teams: Look for collaboration features and user management
  • For privacy: Prefer fully open-source, self-hosted options with no telemetry

Refer to the comparison table above for detailed feature breakdowns.

Can I migrate between these tools?

Most tools support data import/export. Always:

  1. Backup your current data
  2. Test the migration on a staging environment
  3. Check official migration guides in the documentation

Are there free versions available?

All tools in this guide offer free, open-source editions. Some also provide paid plans with additional features, priority support, or managed hosting.

How do I get started?

  1. Review the comparison table to identify your requirements
  2. Visit the official documentation (links provided above)
  3. Start with a Docker Compose setup for easy testing
  4. Join the community forums for troubleshooting
Advertise here