Stalwart

Stalwart 1.0 Says It's the Fastest Mail Server on the Planet. Here Are the Numbers.

After five years, Stalwart hit its first stable release on October 26 with benchmarks claiming it beats Postfix and Dovecot in 35 of 38 head-to-head tests. Every one of those numbers was produced by the vendor.

Stalwart 1.0 Says It's the Fastest Mail Server on the Planet. Here Are the Numbers.
Photo by Brett Jordan / Unsplash

Everybody who's run their own mail server has the same two stories.

There's the week you lost to SPF, DKIM and DMARC. Reading RFCs at one in the morning. Convinced you'd finally nailed it.

And then there's the morning you worked out Gmail had been quietly binning everything you sent for eleven days. Nobody tells you. That's the thing. There's no alert for "the internet has decided you're spam now."

So I come at mail server benchmarks with a very specific kind of tiredness. Throughput has never once been my problem. I have never had a self-hosted mail server fall over because it couldn't handle the load. Not once. They fall over because of deliverability, or spam, or a cert nobody renewed, or a disk nobody watched.

But Stalwart hit 1.0 on October 26, five years after it started life as an email parsing library. And the numbers they're publishing are wild. So let's look at them properly — including the part where I tell you who ran them.

What Stalwart actually is

One binary. Rust. JMAP, IMAP, POP3, SMTP, CalDAV, CardDAV and WebDAV, with a spam filter and clustering built in.

That "all-in-one" bit is the real pitch and it's easy to skim right past. So think about what a traditional self-hosted mail stack actually is. Postfix. Plus Dovecot. Plus Rspamd. Plus OpenDKIM. Plus whatever's doing calendars. Each one with its own config language, its own idea of where logs belong, and its own opinion about what a user is.

Stalwart is one process and one config file.

If you've ever spent an evening working out why Postfix and Dovecot disagreed about whether an account existed, that alone is worth a look. I have. It was not a good evening.

It's AGPL-3.0, by the way — the GitHub release confirms the source of every release stays public under it, though they develop test suites and new features privately before release. There's an Enterprise edition on top.

If you're weighing it against the alternatives, I've already written up how Stalwart compares to Mailcow and Mailu. This piece is about what changed with 1.0, and that comparison predates this release.

The benchmarks, and who ran them

Important caveat first, because it governs everything below.

Every number here was produced by Stalwart, testing Stalwart. Their announcement is upfront about the setup, and they've put scripts, configs and raw results in a public repo — which is more than most vendors bother with. But I couldn't find an independent reproduction of any of it. Treat it as a vendor claim with its homework shown. Not a settled fact.

The setup: Stalwart 1.0 against Postfix 3.11 for SMTP and Dovecot 2.4 for IMAP. Identical Azure boxes — 8 AMD EPYC Zen 5 vCPUs, 32 GiB RAM, local NVMe. Load was 1,000 mailboxes plus three mailboxes holding up to 250,000 messages each. Measured alternately on the same hardware, which is the right way to do it.

Headline: "Stalwart was faster in 35 of the 38 headline comparisons."

Test Stalwart vs incumbent
IMAP everyday operations Faster in 14 of 15, by 1.4x to 19x
IMAP login, full TLS handshake 5.1 ms vs 28 ms
Fetch a 1 MB message 6x faster
Thread INBOX 5x faster
Move or delete 100 messages 18x–19x faster
Incoming SMTP throughput 5,445 vs 723 msg/sec
IDLE notification latency 3.3 ms vs ~0.5 s
Full-text search, 250k-message mailbox 3.75 ms vs 136 ms
Move or delete 25,000 messages ~0.1 s vs 24–34 s

That last row made me sit up. Over 200x. Twenty-four to thirty-four seconds, down to a tenth of one.

If you've ever watched a mail client spin because somebody selected a year of messages and hit delete, that's the gap being described. That's not a benchmark number to me. That's a support ticket I've answered.

Dovecot won three tests. All three involve marking every message in a mailbox as read at once, and Stalwart's announcement says so plainly rather than burying it. I appreciate that more than I expected to.

Why it's fast, which is the bit I actually care about

Benchmarks are marketing. Architecture is engineering. And the architecture here is doing real work.

The mail-parser library got rewritten with SIMD and SWAR techniques. The CPU-specific kernel is picked at runtime, and — this is the detail I respect — each kernel is fuzzed against a scalar reference implementation.

That's the right way to do it. Hand-tuned SIMD is how you get fast. Fuzzing it against a dumb, obviously-correct reference is how you avoid shipping a parser that's fast and wrong. A parser that's wrong on one message in ten thousand is worse than a slow one, because you won't find out for months.

Message bodies only get decoded when something actually reads them. There's a companion library, encodify, doing SIMD kernels for Base64, Base32 and quoted-printable — they claim Base64 MIME decoding is 22x faster than the Rust base64 crate on EPYC.

Then the caching, which is where those IMAP numbers come from. Each account's message index sits in memory in packed form. IMAP sorting, threading, searching and resync get served out of that, with few or no database reads. Big accounts get their own cache tier with a memory budget.

The storage layer handles RocksDB, PostgreSQL, MySQL, SQLite and FoundationDB — each tuned specifically rather than through some generic abstraction. RocksDB gets per-data-type column families and bloom filters, with hot counters spread across up to 256 slots. SQLite gets page size, cache, mmap and WAL settings tuned for mail workloads. Objects are compressed with zstd using dictionaries trained on real email, calendar and contact data.

That's a lot of specific, unglamorous optimisation. It reads like people who measured things. Not people who assumed Rust would be fast because it's Rust — which, for the record, describes most of the Rust rewrites I've been asked to look at.

The migration will eat your Saturday

Here's what I'd lead with if I were writing their release notes.

Stalwart 1.0 will not open a store from an earlier version. The release notes put it as "cannot be upgraded in place." Not "will migrate automatically." Will not open.

So this is a copy, not an upgrade, and the prerequisites matter more than the steps:

  • Source must be on 0.16.27. Not 0.16.anything. If you're on 0.16.26, that's a separate upgrade first.
  • Target must be a brand new empty store — new Postgres or MySQL database, new FoundationDB cluster, new RocksDB directory or SQLite file.
  • You need disk for a second full copy of the data store. Nobody puts this in the headline and it's the one that'll stop you at 11pm. If 1.0 reuses an external blob store, only the data store gets duplicated, but plan for the worst case.
  • Every 0.16 node has to be stoppable for the final run.
  • The tool runs on Linux or macOS and has to reach both stores under the same paths and network names the servers use. If it can't, there's a paths-and-ownership section you'll need to read.

The actual shape of it is nicer than I expected. You install the tool, prepare the empty 1.0 store, then run the copy while 0.16 keeps serving users. You can run it repeatedly — each pass only copies what changed since the last one. Then you stop 0.16, run a final pass, and it finalises and verifies.

So your downtime is one incremental sync, not a full copy. That's a well-designed migration and I want to give credit for it.

There's a verify command that'll check a sample of accounts or the lot. There are status and report commands that'll tell you progress, accounts in error, and anything dropped, changed or not copied. And there's a rollback command that copies the 1.0 store back into a new empty 0.16 store.

Read that last one again, because it's the part that should make you willing to try this at all. You have a documented way back. That is not a given with database migrations and it's worth more than any benchmark on this page.

Before you touch any of it, find out what you're actually running:

# If you installed from the Debian/Ubuntu package
dpkg -l | grep -i stalwart

# If you run it in Docker
docker inspect --format '{{.Config.Image}}' stalwart

That's deliberately generic rather than Stalwart's own CLI. I haven't got the tool's flag syntax in front of me, and I'm not going to guess at it in an article about not breaking your mail server. The migration guide and the SCHEMA_CHANGES.md in the migrate repo have the exact invocations.

Three breaking changes that aren't the database

Clusters now need a Redis-based in-memory store. Running multi-node without Redis in the picture? That's a new dependency to sort out before you start, not after.

Settings got renamed, restructured and removed, and telemetry event identifiers were renumbered. So your config isn't going to carry across clean, and if you've got dashboards or alerts keyed on telemetry events, those break quietly. Quietly is the problem.

OAuth now enforces the OAuth Profile for Open Public Clients. Stalwart's own authorization server supports it. External identity providers might not — and the release notes name a specific casualty: clients such as Apple Mail may stop working.

That's the one I'd test first. "Users can't log in" is bad. "Users on iPhones can't log in and you find out from an angry message on Monday" is worse.

What else landed in 1.0

  • Full-text search inside attachments — PDF, docx/xlsx/pptx, OpenDocument, EPUB, RTF. The extractor is Rust with no external document libraries, enforcing size and complexity limits. That last part matters more than it sounds. Document parsers are a classic memory-exhaustion hole, and writing your own with hard limits beats pulling in a library that'll happily try to allocate a gigabyte because somebody sent you a malformed PDF.
  • Post-quantum DKIM2 signatures using composite ML-DSA-44 and Ed25519. Early, but it's there.
  • JMAP Object Metadata and IMAP METADATA (RFC 5464).
  • OAuth and OIDC work — Client ID Metadata Documents, per-client scopes, revocable connected applications.
  • Operational bits: built-in in-memory store for single-node, secondary LDAP servers, a memory budget, SSRF protection on outbound HTTP, and configurable client IP sources and trusted proxy hops.

Those last two are quiet security wins and I don't want them lost in a bullet list.

SSRF protection on outbound HTTP matters because a mail server fetches things — remote content, webhooks, identity provider endpoints. A server that'll fetch arbitrary URLs on request is a pivot straight into your internal network, and mail servers tend to sit somewhere with more network reach than they strictly need.

And configurable trusted proxy hops is how you stop somebody spoofing X-Forwarded-For to walk around your rate limiting. That's the kind of bug that only surfaces when somebody's actively working on you.

The AI spam classifier is Enterprise-only, supporting OpenAI-compatible, Anthropic, Gemini and Ollama models.

What the benchmarks don't tell you

They measure throughput and latency. They do not measure the things that actually kill self-hosted mail.

Nothing here tells you about deliverability. A server that accepts 5,445 messages a second is irrelevant if Microsoft is silently dropping your outbound. That's a reputation and DNS problem and no amount of SIMD touches it.

Nothing here tells you about spam filtering quality. There's a built-in filter and an Enterprise AI classifier, and I've not seen either benchmarked against Rspamd by anyone.

And nothing tells you about operational maturity. This is version 1.0 of a project that has just changed its database layout. Postfix has been in production since 1998. There is real, unglamorous value in software that thousands of people have already hit every edge case in, and a 1.0 release — however fast — hasn't earned that yet. It can't have. It's only just got here.

I'm not telling you not to run it. I'm telling you "fastest" and "most reliable" are different axes, and self-hosted mail punishes you on the second one.

What I'd actually do

Already on Stalwart? Plan it properly. Get to 0.16.27, check you've got disk for a second copy, test the migration against a copy, check your IdP against the new OAuth profile, add Redis if you're clustered. Know where the rollback command is before you need it. Don't do this on a Friday.

Running Postfix and Dovecot and it works? Honest answer: the performance gap probably doesn't matter to you. A thousand-mailbox server isn't straining either stack. The reason to move is the single-binary operational story, not the benchmarks. Don't let a 200x number talk you into a migration you didn't need.

Building something new? Stalwart's a legitimately interesting option, and attachment search plus built-in CalDAV/CardDAV means one service instead of four. Just go in knowing you're early.

And whatever you pick — the thing that decides whether self-hosted mail works for you is still DNS, reputation and monitoring. I've written about the monitoring side before and I'd argue it matters more here than for almost anything else you self-host.

Because mail fails silently. And by the time a human tells you, it's been broken a week.

$ tail -f /forums

Discussion

Comments live on the community forums — sign in there to reply.