Stalwart 1.0, released on 26 October 2026, is the first stable version of the Rust mail and collaboration server that speaks JMAP, IMAP, POP3, SMTP, CalDAV, CardDAV and WebDAV from one binary. It finalises the database schema so later upgrades can happen in place — which is exactly why this one cannot: a 1.0 server will not open a store created by 0.16 or earlier.
This article covers what changed in 1.0, how to read the project's benchmarks against Postfix and Dovecot (including the caveats the project itself publishes), how to start a fresh 1.0 server in Docker, and the full upgrade path from 0.16 with stalwart-migrate, step by step. It is for people who run their own mail, or are deciding whether to.
| Released | 26 October 2026 (announcement) |
| Language and licence | Rust; AGPL-3.0 for the Community edition, with a proprietary Stalwart Enterprise License for commercial features (GitHub) |
| Protocols | JMAP (with EventSource and WebSocket push), IMAP, POP3, SMTP, CalDAV, CardDAV, WebDAV |
| Data stores | RocksDB, PostgreSQL, MySQL, FoundationDB, SQLite; Redis for in-memory; S3, filesystem or Azure Blob for blobs |
| Upgrade from 0.16 | One-time copy with stalwart-migrate, from 0.16.27 only (guide) |
What is new in Stalwart 1.0?
The headline work is performance and a schema the project commits to keeping. According to the release post, the parsers were rewritten with SIMD and SWAR techniques, a new encodify library handles Base64 and quoted-printable, identifiers are assigned inside the write transaction, objects are zstd-compressed with dictionaries for mail, calendar and contact data, and per-account message indexes are cached in packed form so most IMAP sorting, threading and searching needs few or no database reads.
- Full-text search rewritten: word positions, phrase search, stemming and prefix matching. The project reports a full-text search across a 250,000-message mailbox in under 4 ms.
- Document search: text extraction for PDF, docx, xlsx, pptx, odt, ods, odp, EPUB and RTF attachments, with a Rust extractor that enforces size and complexity limits.
- Metadata: JMAP Object Metadata and IMAP METADATA (RFC 5464).
- DKIM2 signatures with a post-quantum composite of ML-DSA-44 and Ed25519.
- OAuth and OIDC: Client ID Metadata Documents, per-client scopes, a revocable list of connected apps, and opaque-token validation for external providers.
- Operations: a built-in in-memory store for single nodes, secondary LDAP servers, a memory budget for buffered data, and SSRF protection for outbound HTTP.
An AI spam classifier supporting OpenAI-compatible, Anthropic, Gemini and Ollama models ships in the Enterprise edition only. The changelog has the full list.
How fast is Stalwart compared with Postfix and Dovecot?
In the project's own tests, faster in 35 of 38 headline comparisons. The benchmarks repository pits Stalwart 1.0 on RocksDB against Postfix 3.11.7 and Dovecot 2.4.5 on identical Azure Standard_D8ads_v7 servers (8 Zen 5 vCPUs, 32 GiB RAM, local NVMe), alternating the systems run by run and reporting medians. Scripts, configurations and raw results are published.
Source: [stalwartlabs/benchmarks README](https://github.com/stalwartlabs/benchmarks); 0.53 is Dovecot's 1.9× win shown as a ratio
| Speed-up | |
|---|---|
| IMAP login with TLS | 5.5× |
| Fetch a 1 MB message | 6.3× |
| Thread INBOX | 4.9× |
| Move 100 messages | 18× |
| Mark whole INBOX read | 0.5× |
| SMTP receive 4 KB | 12× |
| SMTP relay | 3.6× |
| Search 250k-message mailbox | 36× |
| Test | Stalwart 1.0 | Postfix or Dovecot | Ratio |
|---|---|---|---|
| Inbound SMTP messages per second | 5,445 | 723.1 | 7.5× |
| Relayed messages per second | 1,984 | 516.5 | 3.8× |
| Mixed IMAP sessions per second | 2,178 | 405.0 | 5.4× |
Read these as vendor benchmarks with unusually good disclosure, not as a verdict. The repository's own caveats matter:
- Disk sync differs. Postfix fsyncs each message before accepting it; Stalwart and Dovecot, as configured, do not. That favours Stalwart on SMTP. A full-fsync Dovecot variant is published for write tests.
- Builds differ. Stalwart was compiled for the exact CPU; Postfix and Dovecot are standard builds.
- One server each, cloud VMs and generated message text. No clustering, and storage-backend comparisons are still being prepared.
- Dovecot wins bulk flag changes: marking every message read is 1.9× faster on INBOX and 4.2× faster on a 250,000-message mailbox.
How to run Stalwart 1.0 in Docker
The Docker guide recommends the major tag stalwartlabs/stalwart:v1 over latest, with two named volumes: one for config.json, one for data. The admin port 8080 is bound to localhost because the first-run wizard is not protected against password guessing.
services: stalwart: image: stalwartlabs/stalwart:v1 container_name: stalwart init: true restart: unless-stopped ports: - "25:25" - "465:465" - "993:993" - "995:995" - "4190:4190" - "443:443" - "127.0.0.1:8080:8080" volumes: - stalwart-etc:/etc/stalwart - stalwart-data:/var/lib/stalwart volumes: stalwart-etc: stalwart-data:docker compose up -d # Temporary admin credentials for the setup wizarddocker logs stalwart 2>&1 | grep -A8 'bootstrap mode' # From your machine, tunnel to the localhost-only wizardssh -L 8080:127.0.0.1:8080 your-mail-host# then open http://127.0.0.1:8080/admin and sign in as adminA new bootstrap password is generated on every start in bootstrap mode, so only the latest log block is valid. The wizard's last screen shows the real administrator account, admin@<domain>, and its password once — it is not written to the log. Record it, then docker restart stalwart and sign in at https://<hostname>/admin.
Why upgrading from 0.16 to 1.0 needs a migration
Because 1.0 uses a new database layout and refuses the old one. Start a 1.0 binary on a 0.16 store and you get "The data store was created by a Stalwart version prior to 1.0" (upgrade guide). The project calls this the last disruptive migration; the schema is final from here.
The supported route is stalwart-migrate, which reads the 0.16 store and copies into a new, empty 1.0 store. It runs repeatedly while 0.16 keeps serving users, copying only what changed each time, so the downtime is just the final run. Account and message IDs, IMAP UIDs and change history are preserved.
- Stalwart 0.16.27 → 0.16 store
- 0.16 store → stalwart-migrate: incremental reads
- stalwart-migrate → State dir
- stalwart-migrate → 1.0 store: copy changes
- 1.0 store → Stalwart 1.0
| Area | What happens | What to do |
|---|---|---|
| OAuth tokens | Not migrated | Warn users that OAuth clients will ask them to sign in again |
| Full-text index | Not copied; rebuilt after start | Expect incomplete search results until the rebuild finishes |
| CalDAV, CardDAV, WebDAV | Entity tags change; clients re-fetch once | Expect a one-off sync spike; sync tokens stay valid |
| Settings | About a hundred keys renamed | prepare converts them; review its report |
| Clusters | Coordinator needs a Redis-based in-memory store | Provision Redis before upgrading a cluster |
| External OIDC providers | OAuth Profile for Open Public Clients enforced | Test sign-in; compatibility settings exist at a security cost |
Upgrade Stalwart 0.16 to 1.0, step by step
These steps follow the quick migration guide for a script-installed server on RocksDB. Upgrade to 0.16.27 first if you are on an earlier 0.16 release, and make sure the host has room for a second copy of the data store.
Install stalwart-migrate
installShell curl --proto '=https' --tlsv1.2 -LsSf \ https://github.com/stalwartlabs/stalwart-migrate/releases/latest/download/stalwart-migrate-installer.sh \ | sudo shRelease archives with checksums and
gh attestation verifysupport, and astalwartlabs/stalwart-migrateDocker image, are listed on the installation page. Servers built without the enterprise feature need the--ossvariant.Create the empty 1.0 store
/etc/stalwart-v1/config.jsonJSON { "@type": "RocksDb", "path": "/var/lib/stalwart-v1/"}This file becomes the 1.0 server's configuration later. For PostgreSQL, MySQL or FoundationDB, create a new empty database or cluster instead.
Prepare and review the conversion
prepareShell sudo stalwart-migrate prepare --source /etc/stalwart/config.json \ --state /var/lib/stalwart-migrateIt asks about every conversion that needs a decision. Read
prepare-report.txtin the state directory: it lists every dropped object and changed setting.Copy while 0.16 runs
incremental copyShell sudo stalwart-migrate run --source /etc/stalwart/config.json \ --target /etc/stalwart-v1/config.json --state /var/lib/stalwart-migrate \ --owner "$(id -u stalwart):$(id -g stalwart)" sudo stalwart-migrate status --state /var/lib/stalwart-migrateRepeat as often as you like; it can be interrupted and resumed. Each run copies only changes since the last.
Stop 0.16 and run the final copy
final copyShell sudo systemctl stop stalwart # every 0.16 node sudo stalwart-migrate run --final --source /etc/stalwart/config.json \ --target /etc/stalwart-v1/config.json --state /var/lib/stalwart-migrate \ --owner "$(id -u stalwart):$(id -g stalwart)"It asks you to type
yes, watches the 0.16 store for 30 seconds to confirm nothing is writing, copies the last changes, finalises the store and verifies a sample of accounts.Check the report and start 1.0
reviewShell sudo stalwart-migrate report --state /var/lib/stalwart-migrate --severity error # If the window allows, compare every accountsudo stalwart-migrate verify --full --state /var/lib/stalwart-migrateThen keep a copy of the 0.16 configuration, copy
/etc/stalwart-v1/config.jsonover/etc/stalwart/config.json, and run the 1.0 install script, which replaces the binary and keeps the configuration. In Docker, startstalwartlabs/stalwart:v1with the new configuration and data volume.
- etc/
- stalwart/
- config.json0.16 config, keep a copy
- stalwart-v1/
- config.jsonbecomes the 1.0 config
- stalwart/
- var/lib/
- stalwart/0.16 store, keep until 1.0 has proven itself
- stalwart-v1/new 1.0 RocksDB store
- stalwart-migrate/
- prepare-report.txtdropped objects, changed settings
Keep the 0.16 store and the state directory until you trust 1.0: stalwart-migrate rollback copies a 1.0 store back into a new, empty 0.16 store (rollback).
What about 0.15 or earlier, or moving from Dovecot and Postfix?
stalwart-migrate only accepts 0.16.27. For older versions, per-user moves, or migrating from Dovecot and Postfix, the project points to its migration guide: a migration proxy routes each account to the old or new server through a mapping table, and the Vandelay import/export tool moves accounts one at a time. That path is slower to run but lets you move users in batches with no flag day.
Before you upgrade
- To do: Upgrade 0.16 to 0.16.27 and take a backup of the data store.
- To do: Check free disk for a second copy of the data store (only the data store, if blobs are external).
- To do: Run
prepareand readprepare-report.txtbefore scheduling the window. - To do: Provision Redis first if you run a cluster.
- To do: Test external OIDC sign-in against a 1.0 test instance.
- To do: Tell users that OAuth clients will ask them to sign in again and search will be incomplete for a while.
- To do: Keep the 0.16 store and state directory until 1.0 has run cleanly.
Questions people ask about Stalwart 1.0
What is Stalwart mail server?
An open-source mail and collaboration server written in Rust. One binary serves JMAP, IMAP, POP3 and SMTP, plus CalDAV, CardDAV and WebDAV for calendars, contacts and files.
Is Stalwart free?
The Community edition is AGPL-3.0. Some features, such as the AI spam classifier, are only in the Enterprise edition, which uses a proprietary licence.
Can I upgrade Stalwart 0.16 to 1.0 in place?
No. 1.0 uses a new database layout and will not open a 0.16 store. Use stalwart-migrate, which copies into a new 1.0 store while 0.16 keeps running; future versions are meant to upgrade in place.
Which 0.16 version does stalwart-migrate need?
0.16.27. Upgrade earlier 0.16 releases first; for 0.15 or older, use the migration proxy and Vandelay instead.
How much downtime does the migration need?
Only the final run. Incremental runs copy data while 0.16 serves users, and the final run copies the last changes with 0.16 stopped.
Is Stalwart faster than Dovecot and Postfix?
In the project's published benchmarks it won 35 of 38 headline comparisons, with Dovecot faster at bulk flag changes. They are vendor-run tests with documented caveats, such as Postfix fsyncing every message and Stalwart not.
Which databases does Stalwart support?
RocksDB, PostgreSQL, MySQL, FoundationDB and SQLite for data, Redis for the in-memory store, and S3, the filesystem or Azure Blob Storage for message blobs.
Do users need to do anything after the upgrade?
Users of OAuth clients must sign in again, calendar and contact clients re-fetch their data once, and search results are incomplete until the index rebuild finishes.
Sources and further reading
- Stalwart v1.0: The Fastest Mail Server on the Planet — release announcement
- stalwartlabs/benchmarks — methodology, raw results and caveats
- Upgrading from Stalwart 0.16, the quick migration guide and stalwart-migrate installation
- Docker installation and the migration guide for older versions and Dovecot/Postfix
- OIDC compatibility settings
- Stalwart on GitHub and the changelog
- Linuxiac's coverage of the release
Discussion
Join the discussion
Sign in with Google, Apple or an email code to comment and reply.