Skip to content
Deploying / Stalwart

Stalwart

Installing the Stalwart template -- SMTP, IMAP, JMAP, CalDAV and CardDAV in one server -- from first boot to sending mail.

Stalwart is a mail and collaboration server: SMTP, IMAP, POP3, JMAP, ManageSieve, CalDAV and CardDAV in one binary. It is the template with the most setup after install, because mail involves DNS, certificates and ports that the proxy does not handle.

Image stalwartlabs/stalwart:v0.16.21
Web port 8080, behind kamal-proxy (admin UI, JMAP, DAV)
Health check /healthz/live
Published ports 25, 465, 587, 143, 993, 110, 995, 4190
Volumes etc, data
Singleton yes

Before you start

  • A server of its own if you can. Every port below is published on the machine itself, and two environments on one machine cannot both hold port 25.
  • A hostname for the web admin and JMAP, e.g. mail.example.com. Stalwart builds every URL in its JMAP and OAuth discovery documents from it, so the environment is blocked until it has one.
  • Control of the mail domain’s DNS. KamalCloud never writes DNS records.
  • A plan for outbound port 25. Hetzner, DigitalOcean and AWS block it on new accounts, so sending mail needs a support ticket to the provider or a relay. Receiving is unaffected.

Installing

Templates → Stalwart → Install. Give it a name, a runner, an environment, the server and the domain. Install creates the app and seeds:

  • STALWART_PUBLIC_URL, set to https:// plus the environment’s first domain at each deploy
  • The eight mail ports, in TCP, as the environment’s own. You can add or remove them afterwards under Hosts → Ports
  • The etc and data volumes, registered for backup

It does not deploy. Point an A record for the hostname at the server first.

Ports and the firewall

Port Protocol
25 SMTP, server to server
465 SMTP submission over TLS
587 SMTP submission with STARTTLS
143 / 993 IMAP / IMAP over TLS
110 / 995 POP3 / POP3 over TLS
4190 ManageSieve

kamal-proxy only fronts HTTP, so these are published straight from the container. On an API-managed server, every deploy opens them on the account’s cloud firewall. On a self-hosted machine, opening them in whatever firewall it runs is yours to do.

Because two containers cannot hold the same host port, an environment with published ports deploys by stopping the old container before booting the new one. Every deploy is a short mail outage. Sending servers retry, so inbound mail is delayed rather than lost.

First boot

  1. Deploy.
  2. Open Environment → Logs and look for bootstrap mode. Stalwart prints a temporary administrator password there on its first boot.
  3. Go to https://<your domain>/admin and sign in with it.
  4. Run the setup wizard with automatic TLS switched off. kamal-proxy already holds the certificate for the web hostname.
  5. Copy the permanent administrator password from the wizard’s last screen. It is printed there and nowhere else.
  6. Deploy again so the new configuration takes effect.

Certificates for the mail ports

kamal-proxy holds ports 80 and 443 and gets the certificate for the web admin and JMAP. IMAPS and SMTPS on 993 and 465, and STARTTLS on the others, need a certificate of their own, and the proxy is not in that path.

Stalwart cannot answer an HTTP-01 or TLS-ALPN-01 challenge from behind the proxy, so in its admin UI either:

  • set up ACME with a DNS-01 challenge, giving Stalwart an API token for your DNS provider, or
  • upload a certificate and key you obtained elsewhere – remembering that nothing renews it.

Trusting the proxy

Web requests reach Stalwart from kamal-proxy, so without further configuration every client looks like the proxy’s address. Under the HTTP listener’s settings, trust the proxy’s Docker network so the client address is read from X-Forwarded-For.

DNS for the mail domain

Stalwart shows the records it wants for each domain in its admin UI. Publish them at your DNS provider. Typically:

Record Purpose
A / AAAA for the mail hostname points at the server
MX where other servers deliver mail for the domain
TXT (SPF) which machines may send for the domain
TXT (DKIM) the public key Stalwart signs outgoing mail with
TXT (DMARC) what receivers do with mail that fails the two above
SRV / autoconfig optional; lets mail clients find the server by themselves

Set reverse DNS (the PTR record) for the server’s IP to the mail hostname as well. That is set at the cloud provider, not in your DNS zone, and much of the internet rejects mail from an address whose reverse DNS does not match.

Check DNS on the domain in KamalCloud only checks that the web hostname resolves to the server. It does not check mail records.

Locked out

If no administrator can sign in, add two variables:

Variable Value
STALWART_RECOVERY_MODE 1
STALWART_RECOVERY_ADMIN admin:<a password> – make it a secret

Deploy, sign in with that account, fix what needs fixing, then remove both and deploy again. Leaving recovery mode on leaves a second way in.

Data and backups

  • etc (<app name>-etc) – Stalwart’s configuration
  • data (<app name>-data) – mailboxes, calendars, contacts and indexes in RocksDB. It is marked to stop containers, because RocksDB writes continuously and a live tar of it does not restore. Each backup is a short outage.

Schedule both on their volumes. Back up etc alongside data: a mail store restored without its configuration is not much use.

Upgrading

Change the image tag on the app and deploy. Stalwart’s release notes call out upgrades that migrate the data store; take a backup of both volumes first.

Troubleshooting

  • Blocked: “Stalwart reads its own address” – the environment has no domain. Add one and deploy.
  • No bootstrap password in the logs – the etc volume already holds a configuration from an earlier boot. Use the permanent password, or recovery mode.
  • Mail arrives but none goes out – outbound port 25 is still blocked by the provider.
  • Outgoing mail lands in spam – check SPF, DKIM, DMARC and reverse DNS.
  • Clients warn about the certificate on 993 – the mail listeners have no certificate of their own yet.
  • Deploy fails on a port conflict – another container on the machine holds one of the mail ports.