Stalwart: the lightweight mail server in a single container
Set up Stalwart Mail Server with Docker: SMTP, IMAP, JMAP and spam filter in a single, lean container – the modern alternative to Mailcow.
Table of contents
Your own mail server doesn’t have to consist of two dozen containers. Stalwart packs the complete mail server – SMTP, IMAP, JMAP, spam filter and storage – into a single container written in Rust. That makes it lean, fast and low-maintenance: the modern, lightweight alternative to heavyweights like Mailcow.
Mail servers stay demanding – no matter how lean
What are we building?
By the end, Stalwart 0.16.14 runs as a single Docker container on your server and provides everything a full-featured mail server needs:
- SMTP (send/receive) on ports 25, 465 and 587,
- IMAP (993) and the modern JMAP for fast, efficient clients,
- ManageSieve (4190) for server-side filter rules,
- a built-in spam and phishing filter (no separate Rspamd/ClamAV needed),
- integrated storage (RocksDB) – no external database,
- a modern web interface to manage domains, accounts and settings.
The big difference from Mailcow: it’s one process, one container, a few hundred megabytes of image. That significantly reduces RAM demand, attack surface and maintenance effort. Tested with Docker 29 on Debian 13.
Why is the lean approach worth it? A classic mail server stack consists of a good dozen services – Postfix, Dovecot, Rspamd, a database server, a virus scanner, Redis, a web server – all of which run individually and have to be updated and tuned to each other. Each of them is a potential source of error and a piece of attack surface. Stalwart bundles the same functionality into a single application written in Rust. Rust also brings memory safety out of the box – for software that accepts unfiltered data from the entire internet, that’s a real security argument.
Prerequisites
- A dedicated server with Debian 13 and Docker installed, on which the mail and web ports are free (no reverse proxy occupying 25/443).
- A domain whose DNS you control yourself – for the mail server’s A record and the MX record.
- Outbound port 25 must be open. Many providers block it against spam; at netcup you enable it via a support ticket. Without an open port 25 you can’t deliver mail to other servers.
- A PTR/reverse-DNS entry for the server IP (in the netcup SCP) that points to your mail hostname.
Stalwart is frugal – a small server is enough to start. How much your setup needs in total is estimated by the server calculator.
VPS 1000 G12
4 vCores · 8 GB RAM · 256 GB NVMe
from €10.36/month
Stalwart is lean – the VPS 1000 is quite enough to start.
💶 5 € voucher for new netcup customers:36nc17844976032
(new customers only, no domains)
Step by step
Step 1: Create the DNS records
First set up the DNS entries – they need time to propagate. YOUR_DOMAIN is your mail domain, YOUR_SERVER_IP the server’s IPv4 address:
mail.YOUR_DOMAIN. A YOUR_SERVER_IP
YOUR_DOMAIN. MX 10 mail.YOUR_DOMAIN.The MX record refers other mail servers to mail.YOUR_DOMAIN. This hostname is your server’s identity. SPF, DKIM and DMARC we add in the deliverability tutorial – Stalwart generates the DKIM keys itself during setup.
Step 2: Write the compose.yaml
Create a folder and change into it:
mkdir -p /opt/stalwart && cd /opt/stalwartCreate the file compose.yaml:
services:
stalwart:
image: stalwartlabs/stalwart:v0.16.14
container_name: stalwart
restart: unless-stopped
ports:
- "25:25" # SMTP (server-to-server)
- "465:465" # SMTPS (send, implicit TLS)
- "587:587" # Submission (send, STARTTLS)
- "143:143" # IMAP
- "993:993" # IMAPS
- "4190:4190" # ManageSieve
- "8080:8080" # web interface & JMAP (initial setup)
- "443:443" # HTTPS (after setup, own certificate)
volumes:
- stalwart_data:/opt/stalwart
volumes:
stalwart_data:The most important thing: one service, one volume for all data. Stalwart fetches its TLS certificate later itself via Let’s Encrypt – you don’t need a separate reverse proxy for that. Make sure port 443 is free on the host (Stalwart also uses it for the certificate retrieval, see step 4).
Step 3: Start the container and get the startup password
Start Stalwart:
docker compose up -dOn the very first start, Stalwart runs in bootstrap mode: there’s no configuration yet, and port 8080 is open for the initial setup. In the process it generates a one-time administrator password that you read from the logs:
docker compose logs | grep -A2 "temporary administrator"🔑 Stalwart bootstrap mode - temporary administrator account
password: <ONE-TIME-PASSWORD>Note this password – it’s shown only once. (If you’d rather set a fixed password, you can store a STALWART_RECOVERY_ADMIN=admin:YOUR_PASSWORD under environment: in the compose.yaml.)
Step 4: Log in and run through the setup wizard
Open the web interface in the browser: http://YOUR_SERVER_IP:8080/admin. You land on the login. Log in as user admin with the one-time password from step 3 – the input is done in two steps (first username, then password):

After logging in, the setup wizard greets you. In the first step you set the server identity:
- Server Hostname:
mail.YOUR_DOMAIN– the FQDN from your A record. - Default Email Domain:
YOUR_DOMAIN– your mail domain. - Automatically Obtain TLS Certificate – leave enabled; Stalwart fetches the Let’s Encrypt certificate itself. For that, port 443 must be reachable from outside: Stalwart uses the TLS-ALPN-01 challenge over 443 by default, it doesn’t need port 80 for it.
- Generate Email Signing Keys – leave enabled; that generates your DKIM keys right away.

Click through the further steps of the wizard:
- Storage: the default RocksDB is an embedded key-value store and perfect for single-server setups – no external database server needed. Only for large, clustered installations do you reach for PostgreSQL or similar.
- Directory: the internal directory manages accounts and passwords directly in Stalwart. Whoever already runs a central LDAP/Active Directory can connect it here instead – for getting started you stay with the internal directory.
- Logging: defines how verbosely Stalwart logs.
Infois a good compromise in everyday use; on problems you briefly turn up toDebug. - DNS: for the start, manual management is enough – Stalwart shows you the records to create later under the respective domain.
At the end, Stalwart generates your permanent administrator account and shows its password a single time – write it down immediately. From now on the bootstrap mode is over and port 8080 is no longer open for setup; you reach the console in future via HTTPS.
Already using a reverse proxy?
Step 5: Create a domain and first mailbox
After the setup you’re in the management console. Via the left navigation you reach Directory → Domains and Directory → Accounts. If needed, create further domains and then your first mailbox:

Via Create account you create a mailbox – email address, display name and a strong password (password manager!). This account can then log in via IMAP/JMAP and SMTP:

Step 6: Set it up in a mail program
Enter the new account in your mail program. Stalwart supports autodiscovery, so usually the email address and password are enough. Manually you use:
IMAP: mail.YOUR_DOMAIN, port 993, SSL/TLS
SMTP: mail.YOUR_DOMAIN, port 587, STARTTLS
User: the full email addressBesides the tried-and-true IMAP, Stalwart also speaks JMAP – a modern, JSON-based protocol designed for today’s networks. JMAP transfers only real changes instead of re-syncing whole folders, gets by with a single connection and is thus noticeably more sparing with battery and data – especially on the phone. If your mail client supports JMAP (e.g. the Thunderbird family in newer versions), you benefit directly; otherwise you stay with IMAP, which works just the same.
A mail from you to yourself should land in the inbox immediately – that proves that local delivery, IMAP and SMTP are in place. The acid test, though, is sending to the outside and receiving from the outside, and there deliverability decides.
Note
Step 7: Spam filter and Sieve rules
A big advantage of Stalwart: the spam and phishing filter is already built in and active – you don’t have to run and maintain a separate Rspamd container. It rates incoming mails based on numerous features (reputation, SPF/DKIM/DMARC result, content heuristics) and marks or blocks suspicious ones. In the console under Settings → Spam Filter you can adjust the sensitivity and put senders on allow or block lists.
For your own rules on incoming and outgoing mail, Stalwart supports Sieve – the standardized filter language. With it you sort e.g. newsletters automatically into a folder or forward certain senders. You manage Sieve scripts server-side (via ManageSieve on port 4190) or directly in the web interface, so the rules take effect independent of the mail program – even when your computer is off.
Tip
When things go wrong
The web interface on port 8080 doesn’t respond. the container is still booting or port 8080 is
occupied/blocked. check docker compose ps, read docker compose logs and make sure the
firewall lets port 8080 (and later 443) through.
I missed the bootstrap password. it’s only logged once. set a
STALWART_RECOVERY_ADMIN=admin:YOUR_PASSWORD under environment: in the compose.yaml and restart
with docker compose up -d – that gives you a fixed recovery-admin account.
No TLS certificate, the HTTPS address shows a warning. Let’s Encrypt can’t reach your server on
port 443 (Stalwart uses the TLS-ALPN-01 challenge over 443), or the A record of mail.YOUR_DOMAIN
is wrong. check the A record and open port 443 in the firewall – Stalwart retries the certificate
request automatically afterwards.
Mails to the outside stay stuck, logs show timeouts on port 25. your provider blocks outbound SMTP traffic on port 25. have port 25 enabled outbound at the provider (at netcup via a ticket). Without it, no delivery to other servers is possible.
Other servers don’t accept your mails or they land in spam. missing or wrong PTR entry, no SPF/DKIM/DMARC. that’s not a Stalwart error but a matter of DNS/reputation configuration – see the deliverability tutorial.
Maintenance & backups
Updates. Stalwart is a single container – an update is correspondingly simple:
cd /opt/stalwart
docker compose pull && docker compose up -dBefore bigger jumps, read the release notes on GitHub; stay on a specific version tag (like v0.16.14 here) instead of latest so updates remain reproducible.
Backups. All data – messages, accounts, configuration and the DKIM keys – lives in the stalwart_data volume. Back up this volume regularly away from the server, cleanest encrypted with Restic. Because Stalwart writes the data during operation, you back up most consistently by briefly stopping the container (docker compose stop), backing up the volume and starting again. The DKIM keys are especially important: if they’re lost, your signatures break after a restore until you reset the DNS records. A backup you’ve never restored is just a hopeful guess – test the restoration once on a test system.
Keep an eye on reputation. Occasionally check whether your server IP has landed on a block list, and take a look at the Stalwart console under Observability. A compromised mailbox that sends spam quickly ruins the reputation of your entire domain.
Send feedback: feedback@serverkueche.de
You might also like

Your own mail server with Mailcow: setup from scratch
Set up Mailcow (dockerized) on your own server: Postfix, Dovecot, Rspamd and SOGo webmail under your domain – a complete …

Email deliverability: SPF, DKIM, DMARC and PTR done right
So your self-hosted mails land in the inbox instead of spam: reverse DNS, SPF, DKIM and DMARC explained, set up and …

Host Your Own Website with Hugo – Like Serverküche Itself
Build a static website with Hugo and serve it from a Docker container behind Traefik – fast, secure, no database. …