Stalwart is a perfect replacement for exim+dovecot
Introduction
This is a small guide on how to set up a secure email server.
Problem Statement
In today’s world, having an email address is almost essential for a convenient life, unless you’re living under a rock.
What Are the Options?
- Free email providers - Gmail, Outlook, Yahoo, Mail.com, etc.
- Secure email providers - ProtonMail, Tuta, etc.
- Paid email providers - Similar to the above but with more storage and features.
- Hosting your own email server - Full control but requires effort.
Downsides of Using Third-Party Providers
- Lack of control & trust issues – You depend on the provider’s policies and infrastructure.
- No transparency – You cannot audit what happens on the server.
- Protocol restrictions – OAuth enforcement and other imposed limitations.
- Privacy concerns – Emails are likely monitored for ad targeting.
Upsides of Using Third-Party Providers
- No need to worry about backups, setup, DNS management, or maintenance.
A widely used concept in business and strategic planning is “Owning the Ground” or “Positional Advantage.” I believe individuals should apply this principle as well. Setting up and maintaining your own email server offers significant control and security benefits in exchange for some effort.
Requirements
Our setup must meet the following criteria:
- Open-source server implementation
- TLS-encrypted IMAP/SMTP
- Server-side encrypted emails
- Anti-spam measures (DMARC, SPF, etc.)
Overall Strategy
I previously experimented with Exim and Dovecot, and while they worked well, they are complex, legacy systems that require deep technical knowledge.
Recently, I discovered Stalwart, a modern and secure email server that meets all our requirements. It even supports server-side encryption using your public PGP key. This means your emails remain encrypted on the server, even if the sender doesn’t use PGP. While emails can still be intercepted on the sender’s server or during server-to-server communication, this setup provides a much higher level of security than relying on a third party.
Setup steps
- register DNS name (~15$ a year)
- rent VPC server (~5$ a month)
- install linux (your choice, should run docker and firewall)
- config docker
Let’s disable firewall control and setup reasonable docker nets.
File /etc/docker/daemon.json
{
"experimental": false,
"iptables": false,
"ip6tables": false,
"default-address-pools": [
{
"base": "172.30.0.0/16",
"size": 24
},
{
"base": "172.31.0.0/16",
"size": 24
}
]
}
- run stalwart We want to have static IP for firewall and nginx traffic forwarding.
docker run --ip 172.30.0.2 -d -ti -v /opt/stalwart-mail:/opt/stalwart-mail --name stalwart-mail stalwartlabs/mail-server:v0.10.2
- config nginx
File: /etc/nginx/sites-enabled/mail.yourserver.com.conf
server {
listen CHANGE_ME_TO_SERVER_IPV4:80;
listen [CHANGE_ME_TO_SERVER_IPV6]:80;
server_name mail.yourserver.com www.yourserver.com;
location ~* "^/.well-known/acme-challenge/*" {
proxy_pass http://172.30.0.2:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
allow all;
}
location / {
return 301 https://$host$request_uri;
}
location ~ /\. {
deny all;
}
}
server {
listen CHANGE_ME_TO_SERVER_IPV4:443 ssl http2;
listen [CHANGE_ME_TO_SERVER_IPV6]:443 ssl http2;
server_name mail.yourserver.com www.mail.yourserver.com;
root /srv/www/mail.yourserver.com;
access_log /var/log/nginx/mail-access.log;
error_log /var/log/nginx/mail-error.log;
ssl_certificate /etc/letsencrypt/live/mail.yourserver.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mail.yourserver.com/privkey.pem;
ssl_session_cache shared:SSL:15m;
ssl_session_timeout 120m;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
error_page 403 /custom_403.html;
location = /custom_403.html {
internal;
}
# restrict admin UI
location / {
allow YOUR_HOME_IPV6;
allow YOUR_HOME_IPV4;
deny all;
proxy_pass http://172.30.0.2:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# cache wasm, it's large
location ~* ^.+\.(wasm|js|css|swf|xml|txt|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
allow YOUR_HOME_IPV6;
allow YOUR_HOME_IPV4;
deny all;
add_header Cache-Control "public, max-age=31536000, immutable";
proxy_pass http://172.30.0.2:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ /\. {
deny all;
}
}
- get tls ssl cert/ nginx port forward? use certbot to get ssl certs for nginx and stalwart UI to get a copy to stalwart, it’s not optimal and needs some way of sharing certs.
certbot certonly -d mail.yourdomain.com --standalone --agree-tos -m postmaster@yourmail.com -n
renew
certbot renew
handy Makefile
run-mail:
docker run --ip 172.30.0.2 -d -ti -v /opt/stalwart-mail:/opt/stalwart-mail --name stalwart-mail stalwartlabs/mail-server:v0.10.2
cert-renew:
systemctl stop nginx
certbot renew
systemctl start nginx
- update MX records Stalwart Admin web UI provides all necessary DNS records and that’s very helpful and significantly simplifies setup.
https://mail.yourdomain.com/manage/dns/yourdomain.com/view
- enable PGP encryption
- enable +emails
- setup email client
- manual backup? manual cert update?
Configuration files
Future Improvements
After party
I hope this guide helps you set up a router that you have full control over and that gives you confidence. I’d love to hear your feedback and improve the guide further.
Thank you!
P.S.: Good old human paranoia never fails. –>