Stalwart Mail Server
Note: due to the excessive hardware requirements of Stalwart i gave up after two weeks of trying to have it run decently. It works, but any operation on your accounts, specially on huge accounts, will be a massive hit on your I/O. If you do not have SSDs, just forget it.
Stalwart Step into the future with Stalwart, the open-source e-mail powerhouse blending modern features with unparalleled security, speed, and scalability.
I choose Stalwart because it's a new approach to serving mail. Instead of a bunch of interconnected tools, which are often a mess to setup, it's a one piece written from the ground up with a modern approach to email.
Please check this page to understand the choices done in this page. I assume that you are installing the email server on your external server, and not on the home server.
Storage backends
Stalwart is highly I/O dependent and will bring to a crawl itself on anything not equipped with SSDs or high end storage. Choosing the correct storage backend can help, but is no solution for low-end VPS and such hardware.
I suggest, on such devices, to ditch the default RocksDB and use PotgreSQL (or MariaDB) for data and blobs, and setup a Redis backend for the in-memory store.
As a last warning: remember to switch your internal directory storage backed as well!
Note: storage migration is a pain and require a full export/import of all your accounts, which can take days.
Installation
Gentoo ships with a reasonably recent release of Stalwart, but i prefer to have finer control over it, so i prefer to install on bare-metal manually. The project also offer a bare-metal install approach which (see here) which i don't like because it involve download and run an install.sh script, which is a no way for me.
I have downloaded the script (and i suggest you do the same) and inspected it, so the following instructions are directly taken from the install script, but adapted to my setup.
Note: we are installing on the external server, not on the home server!
As usual, first of all create the user:
useradd -m stalwart
Then download the latest release from here for your architecture, be sure to download both the mail server and the cli executable:
su - stalwart mkdir bin etc logs chmod -R 755 /home/stalwart wget 'https://github.com/stalwartlabs/mail-server/releases/download/vX.Y.Z/stalwart-mail-x86_64-unknown-linux-gnu.tar.gz' wget 'https://github.com/stalwartlabs/mail-server/releases/download/vX.Y.Z/stalwart-cli-x86_64-unknown-linux-gnu.tar.gz' cd bin tar xvf ../stalwart-mail-x86_64-unknown-linux-gnu.tar.gz tar xvf ../stalwart-cli-x86_64-unknown-linux-gnu.tar.gz chmod +x stalwart-mail stalwart-cli setcap 'cap_net_bind_service=ep' stalwart-mail
The setcap is necessary to let stalwart open ports in the reserved range (<1024).
Well, it's time to initialize Stalwart:
/home/stalwart/bin/stalwart-mail --init /home/stalwart ✅ Configuration file written to /home/stalwart/etc/config.toml 🔑 Your administrator account is 'admin' with password 'XxxXxXXxX'. chmod 700 /home/stalwart/etc/config.toml
Note the chmod to ensure the config file is not readable by anybody
Take note of the password! You will never see it again.
Now, start the server for the first time:
su - stalwart # ensure you are stalwart user! /home/stalwart/bin/stalwart-mail --config=/home/stalwart/etc/config.toml
Open up your browser and go to http:<external-server-ip>:8080 and login with the credentials above, then immediately head to http:<external-server-ip>:8080/account/password and change the password to something you will remember.
Configuration
How to configure Stalwart can be found here and here. I will highlight some important steps here.
You need to setup the proper hostname in the server configuration.
You should disable the listeners for services you don't need (like POP3!).
You need to create at least one domain. This will also provide you with a full DNS setup that you must setup in your DNS provider.
You need to create email accounts for the domain.
TLS (SSL) certificates are mandatory, and you have a few ways to get them from Let's Encrypt. I use the standard HTTP challenge, which only requires a webserver.
Reverse Proxy
Stalwart has a management web GUI. You should either disable it, or protect it behind a reverse proxy with a configuration like the following:
- stalwart.conf
server { server_name mail.mydomain.com; listen 443 ssl; location / { proxy_pass http://127.0.0.1:8080; proxy_redirect default; 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-Host $server_name; proxy_set_header X-Forwarded-Proto $scheme; } ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; }
In Stalwart, disable the HTTPS listener (you will be using the reverse proxy anyway) and make sure to bind the 8080 listener to 127.0.0.1!
Certificates
TLS certificates are mandatory for correct email operations.
There are three challenges that you can use to generate your certs and none fit my bill:
- The DNS challenge cannot be used because my DNS provider does not support APIs
- The other two challenges both require a web server with open ports (80 + 443), which i do have, but it points to the home, internal, server and not the external server.
The solution is to generate the certificate on the internal server using the classic HTTP Challenge, then move the certs to the external server. For this i have edited the internal server crontab to tar&move the certs once generated, and the external server to upload those certs and unpack for Stalwart.
Internal server crontab:
47 5 * * * /etc/letsencrypt/certbot-renew.sh && (cd /etc && tar cJf /home/user/certs-copy.tar.xy letsencrypt) &>> /root/certbot.log 31 16 * * * /etc/letsencrypt/certbot-renew.sh && (cd /etc && tar cJf /home/user/certs-copy.tar.xy letsencrypt) &>> /root/certbot.log
External server crontab:
10 6 * * * sftp user@10.0.0.1:/home/user/certs-copy.tar.xy /root/certs-copy.tar.xy && cd /etc && tar xvf /root/certs-copy.tar.xy && chown stalwart:stalwart -R letsencrypt 50 16 * * * sftp user@10.0.0.1:/home/user/certs-copy.tar.xy /root/certs-copy.tar.xy && cd /etc && tar xvf /root/certs-copy.tar.xy && chown stalwart:stalwart -R letsencrypt
For this to work, you need user to be able to ssh into the wireguard tunnel without password. 10.0.0.1 is the wireguard tunnel between the external and the internal server. See here for more details.
In Stalwart, setup the certs like this:
%{file:/etc/letsencrypt/live/mydomain.com/fullchain.pem}% %{file:/etc/letsencrypt/live/mydomain.com/privkey.pem}%
Autostart
Since i love the simplicity of OpenRC, create the following script under /etc/init.d/stalwart:
- stalwart
#!/sbin/openrc-run # Copyright 2025 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 name="stalwart daemon" description="email server" pidfile="/run/silverbullet.pid" command_background=true command="/home/stalwart/bin/stalwart-mail" command_args="--config=/home/stalwart/etc/config.toml" command_user="stalwart:stalwart" depend() { need net }
make it executable and add to default runlevel:
chmod +x /etc/init.d/stalwart rc-update add stalwart default