The README keeps what the tool is, how to install it and the first commands, and points to the guide on docs.ihasmail.org. Everything else moves, whole, into docs/ and CONTRIBUTING.md, where it is organized for readers who want the detail. Where the old README disagreed with the code, the code wins.
stalwart-migrator
In-place upgrade tool for Stalwart Mail Server, 0.15.5 → 0.16: no data loss, a checkpoint at every step so an interrupted run resumes instead of restarting, and automated validation that the server still works afterwards. Go, standard library only.
A companion to ihasmail, a JMAP-first webmail client for Stalwart. That one is what you read your mail in; this one gets the server underneath it onto a version that speaks the protocol it needs.
Caution
A migration cannot be undone, and this tool does not undo one. Recovery from a failed migration is your own snapshot or backup, taken beforehand and checked.
runwill not start until you confirm you have one. See Recovery.
Full guide: docs.ihasmail.org/install/stalwart-migrator walks through the whole upgrade — what to fix first, rehearsing, running it, and what to check afterwards.
Requirements
- Stalwart 0.15.5, as a systemd service or a single Docker container
- root on the mail server
python3, for Stalwart's ownmigrate_v016.pystalwart-cli1.0.2 or later, a separate download from the server- an administrator account in Stalwart's directory — not the
fallback-adminfromconfig.toml, which does not survive the migration - Go 1.26 or newer, to build
Fix these on the server first
Both stop a migration, and preflight refuses on both:
- Tenants that share a domain. v0.16 requires an account in a tenant to
use only that tenant's domains.
stalwart-migrate tenantsshows who owns what. - A config-file admin. Migrate as a directory account with the admin role,
whose local part is unique and whose rights don't come only from
tenant-admin.
Details: Known Stalwart problems.
Build
git clone https://github.com/Coffey-Labs/stalwart-migrator.git
cd stalwart-migrator
go build -o stalwart-migrate ./cmd/stalwart-migrate
Use
Give the admin password with --admin-password or
STALWART_MIGRATE_ADMIN_PASSWORD.
# 1. Read-only checks and a migration plan
sudo ./stalwart-migrate preflight --admin-url https://mail.example.com --admin-user [email protected]
# 2. Read-only: convert your settings and report what won't carry over
sudo ./stalwart-migrate rehearse --admin-url https://mail.example.com --admin-user [email protected]
# 3. Rehearse the real migration on a clone of the server (strongly recommended)
# 4. Migrate, once you have a snapshot you have checked you can restore
sudo ./stalwart-migrate run --admin-url https://mail.example.com --admin-user [email protected] \
--recovery-point-confirmed --yes
# Afterwards
sudo ./stalwart-migrate status <run-id> # which steps completed
sudo ./stalwart-migrate report <run-id> # what validation found
A Docker container also needs --container-path-unproven and --target-image;
see Docker deployments. After the migration, check the
certificate on ports 993 and 465, and recreate your ACME provider — Stalwart's
converter drops it without saying so.
Status
Every command works: preflight, rehearse, run, tenants, status and
report. It has migrated a production server (0.15.5 → 0.16.19, 8 seconds of
downtime) and, in another operator's hands, three more. Two things are still
open: some store migrations need one more recovery-mode boot, which is a manual
step, and the Docker path has never completed a migration against a real
Stalwart image. Details and field reports: Status.
Documentation
| Upgrade guide | The whole upgrade, step by step, on docs.ihasmail.org |
| docs/rehearsal.md | Running preflight safely, what rehearse reports, rehearsing on a clone |
| docs/docker.md | Container deployments: flags, what cutover carries and refuses, where the config goes |
| docs/known-stalwart-problems.md | Tenants, the admin account, dropped ACME, certificates on mail ports, the extra recovery boot |
| docs/recovery.md | Why recovery is your snapshot, what the tool keeps, never booting recovery mode again |
| docs/status.md | Command and package state, validation, field reports |
| ARCHITECTURE.md | The design: phases, checkpoints, and the reasoning behind them |
| CONTRIBUTING.md | Building, testing, and where the design is written down |
License
Copyright (C) 2026 Coffey Labs. GPL-3.0-or-later: free software, with no
warranty. The full text is in LICENSE.
No third-party code is vendored — the tool is standard library only, and the
migrate_v016.py it downloads at runtime is Stalwart's own script, fetched
rather than redistributed.