Deploy Stalwart | (Just Updated) Mail Server with JMAP, IMAP and SMTP, Generated Admin Login
Stalwart mail server: SMTP, IMAP, JMAP API, generated admin login
stalwart
Just deployed
/var/lib/stalwart
Deploy and Host Stalwart on Railway
Built for an LLM agent to deploy and use. There is no setup wizard and nothing to fill in: the admin secret is generated at deploy time, the server is configured on first boot by a script in the start command, the contract below lists every URL and variable name, and one working script reads the domain and its DKIM record over the JMAP API.
Stalwart is an open-source mail and collaboration server written in Rust: SMTP, IMAP, JMAP, CalDAV, CardDAV and WebDAV in one binary, with built-in spam filtering, DKIM and a management API. This template runs the official stalwartlabs/stalwart image pinned by digest, with its data on a Railway volume so mailboxes and configuration survive redeploys.
About Hosting Stalwart
This template runs one service, the official stalwartlabs/stalwart image (pinned by digest, volume at /var/lib/stalwart). On first boot the start command starts Stalwart in its bootstrap mode, sets the server hostname and default mail domain to the Railway public domain, creates the RocksDB data store on the volume, generates DKIM keys and restarts Stalwart into normal mode. The admin account is pinned to the generated STALWART_ADMIN_SECRET through Stalwart's recovery-admin setting, so it is the same on every redeploy. A marker file (/var/lib/stalwart/etc/config.json) on the volume stops the setup from running twice.
Stalwart's web interface and JMAP API share the public HTTP port (8080). The mail protocols listen on the same container for other services in the project: SMTP on 25, SMTPS on 465, IMAPS on 993, POP3S on 995 and ManageSieve on 4190, reachable at stalwart.railway.internal. The template does not add a public TCP proxy, because Railway cannot run an HTTP healthcheck on a service that has one. Receiving mail from the internet needs a TCP proxy to port 25 added by you, and many hosting platforms restrict outbound port 25, so use an external relay for sending if your platform does.
Agent contract
- Public URL:
https://, on thestalwartservice (setSTALWART_URLto it); JMAP session at/jmap/session, API at/jmap/ - Private URL:
http://stalwart.railway.internal:8080(SMTPstalwart.railway.internal:25, IMAPS:993) - Credentials: user
admin, secret inSTALWART_ADMIN_SECRETon thestalwartservice - Auth: HTTP Basic with
adminandSTALWART_ADMIN_SECRETon/jmap/sessionand/jmap/; JMAP method names for server settings start withx:(for examplex:Domain/get,x:Account/set) and need"urn:stalwart:jmap"inusing - Health:
GET /healthz/livereturns 200 with no credentials
# run with STALWART_URL and STALWART_ADMIN_SECRET (stalwart service); needs curl and jq
curl -s -o /dev/null -w 'health %{http_code}\n' "$STALWART_URL/healthz/live"
ACC=$(curl -fsS -u "admin:$STALWART_ADMIN_SECRET" "$STALWART_URL/jmap/session" | jq -r '.primaryAccounts["urn:ietf:params:jmap:mail"]')
curl -fsS -u "admin:$STALWART_ADMIN_SECRET" -H 'Content-Type: application/json' "$STALWART_URL/jmap/" \
-d "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Domain/get\",{\"accountId\":\"$ACC\"},\"0\"]]}" \
| jq -r '.methodResponses[0][1].list[0] | .name, (.dnsZoneFile | split("\n")[0])'
A fresh deploy prints health 200, then the mail domain (the Railway public domain) and the first line of its generated DNS zone file, the Ed25519 DKIM TXT record. The full dnsZoneFile field lists the MX, SPF and DMARC records too. Add your own domains and mailboxes through the same API (x:Domain/set, x:Account/set) or the web interface at the public URL, signing in as admin.
Why Deploy Stalwart on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying Stalwart on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.
- Agent-ready - generated admin secret, a contract block and one working script; no setup wizard.
- Configured at boot - hostname, default domain, data store and DKIM keys are set by the first start, with no temporary admin to find in the logs.
- Stable admin - the admin login is the same after every redeploy.
- State survives redeploys - configuration and mail live on the volume.
Common Use Cases
- Transactional and app mail - give other services in the project a private SMTP server.
- Agent mailboxes - let an AI agent create mailboxes and read mail through the JMAP API.
- Self-hosted team mail - IMAP, calendars and contacts without a hosted mail provider.
Dependencies for Stalwart Hosting
- A volume at
/var/lib/stalwart(included in this template).
Deployment Dependencies
- stalwartlabs/stalwart - upstream project
- stalwartlabs/stalwart on Docker Hub - the image this template deploys, tag v1.0.0
Template Content
stalwart
stalwartlabs/stalwart:v1.0.0