---
title: "Deploy Stalwart | (Just Updated) Mail Server with JMAP, IMAP and SMTP, Generated Admin Login"
description: "Stalwart mail server: SMTP, IMAP, JMAP API, generated admin login"
category: "Other"
url: https://railway.com/deploy/stalwart-or-just-updated-mail-server-wit
---

# Deploy Stalwart | (Just Updated) Mail Server with JMAP, IMAP and SMTP, Generated Admin Login

Stalwart mail server: SMTP, IMAP, JMAP API, generated admin login

**[Deploy Stalwart | (Just Updated) Mail Server with JMAP, IMAP and SMTP, Generated Admin Login on Railway](https://railway.com/template/stalwart-or-just-updated-mail-server-wit)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/stalwart-or-just-updated-mail-server-wit/manifest.json

- **Creator:** SuperSlowSloth
- **Category:** Other

## Template content

### stalwart

- **Image:** stalwartlabs/stalwart:v1.0.0@sha256:baafeacd6cbde77acdd94af49972d41c6709e54a376df48e18a9fe3f445eb3d5
- **Start command:** `/bin/sh -c 'exec bash -c "$(echo RD0vdmFyL2xpYi9zdGFsd2FydApVSURHPTIwMDAKbWtkaXIgLXAgIiREL2V0YyIgIiREL2RhdGEiCmNob3duIC1SICRVSURHOiRVSURHICIkRCIKZXhwb3J0IFNUQUxXQVJUX1JFQ09WRVJZX0FETUlOPSJhZG1pbjoke1NUQUxXQVJUX0FETUlOX1NFQ1JFVH0iCkhPU1Q9IiR7UkFJTFdBWV9QVUJMSUNfRE9NQUlOOi1sb2NhbGhvc3R9IgpzdGFydCgpIHsKICBzZXRwcml2IC0tcmV1aWQ9JFVJREcgLS1yZWdpZD0kVUlERyAtLWNsZWFyLWdyb3VwcyAvdXNyL2xvY2FsL2Jpbi9zdGFsd2FydCAtLWNvbmZpZyAiJEQvZXRjL2NvbmZpZy5qc29uIiAmCiAgUElEPSQhCn0KdHJhcCAna2lsbCAtVEVSTSAkUElEIDI+L2Rldi9udWxsOyB3YWl0ICRQSUQ7IGV4aXQgMCcgVEVSTSBJTlQKc3RhcnQKaWYgWyAhIC1mICIkRC9ldGMvY29uZmlnLmpzb24iIF07IHRoZW4KICBmb3IgaSBpbiAkKHNlcSAxIDkwKTsgZG8gY3VybCAtZnMgaHR0cDovLzEyNy4wLjAuMTo4MDgwL2hlYWx0aHovbGl2ZSA+L2Rldi9udWxsIDI+JjEgJiYgYnJlYWs7IHNsZWVwIDE7IGRvbmUKICBCT0RZPSd7InVzaW5nIjpbInVybjppZXRmOnBhcmFtczpqbWFwOmNvcmUiLCJ1cm46c3RhbHdhcnQ6am1hcCJdLCJtZXRob2RDYWxscyI6W1sieDpCb290c3RyYXAvc2V0Iix7ImFjY291bnRJZCI6ImQzMzMzMzMiLCJ1cGRhdGUiOnsic2luZ2xldG9uIjp7ImRlZmF1bHRIb3N0bmFtZSI6IickSE9TVCciLCJkZWZhdWx0RG9tYWluIjoiJyRIT1NUJyIsInJlcXVlc3RUbHNDZXJ0aWZpY2F0ZSI6ZmFsc2UsImdlbmVyYXRlRGtpbUtleXMiOnRydWUsImRhdGFTdG9yZSI6eyJAdHlwZSI6IlJvY2tzRGIiLCJwYXRoIjoiJyREJy9kYXRhLyJ9fX19LCIwIl1dfScKICBPVVQ9JChjdXJsIC1zIC11ICJhZG1pbjoke1NUQUxXQVJUX0FETUlOX1NFQ1JFVH0iIC1IICdjb250ZW50LXR5cGU6IGFwcGxpY2F0aW9uL2pzb24nIGh0dHA6Ly8xMjcuMC4wLjE6ODA4MC9qbWFwIC1kICIkQk9EWSIpCiAgY2FzZSAiJE9VVCIgaW4KICAgIConInVwZGF0ZWQiJyopIGVjaG8gInN0YWx3YXJ0IGJvb3RzdHJhcCBvayBmb3IgJEhPU1QiIDs7CiAgICAqKSBlY2hvICJzdGFsd2FydCBib290c3RyYXAgRkFJTEVEOiAkKGVjaG8gIiRPVVQiIHwgaGVhZCAtYyA0MDApIiA7OwogIGVzYWMKICBraWxsIC1URVJNICRQSUQ7IHdhaXQgJFBJRAogIHN0YXJ0CmZpCndhaXQgJFBJRAo= | base64 -d)"'`
- **Health check:** /healthz/live
- **Public domain:** Yes

## Documentation

# 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 the `stalwart` service (set `STALWART_URL` to it); JMAP session at `/jmap/session`, API at `/jmap/`
- Private URL: `http://stalwart.railway.internal:8080` (SMTP `stalwart.railway.internal:25`, IMAPS `:993`)
- Credentials: user `admin`, secret in `STALWART_ADMIN_SECRET` on the `stalwart` service
- Auth: HTTP Basic with `admin` and `STALWART_ADMIN_SECRET` on `/jmap/session` and `/jmap/`; JMAP method names for server settings start with `x:` (for example `x:Domain/get`, `x:Account/set`) and need `"urn:stalwart:jmap"` in `using`
- Health: `GET /healthz/live` returns 200 with no credentials

```bash
# 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](https://github.com/stalwartlabs/stalwart) - upstream project
- [stalwartlabs/stalwart on Docker Hub](https://hub.docker.com/r/stalwartlabs/stalwart) - the image this template deploys, tag v1.0.0


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/stalwart-or-just-updated-mail-server-wit
