Files
stalwart-migrator/README.md
T
jcoffey-dev cccfa6a2af
ci / test (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 57s
ci / release (pull_request) Skipped
ci / announce (pull_request) Skipped
Name Coffey Labs LLC as the copyright holder
Coffey Labs is now Coffey Labs LLC, an Arizona limited liability company. Copyright lines and SPDX-FileCopyrightText headers naming Coffey Labs or John Coffey now name Coffey Labs LLC. Upstream copyright notices are unchanged.
2026-10-05 23:33:14 -07:00

6.0 KiB

stalwart-migrator

Latest release License: GPL-3.0-or-later Docs: docs.ihasmail.org

Note

Development happens on git.coffeylabs.org/coffey-labs/stalwart-migrator; the copy on GitHub is a read-only mirror. Report issues at git.coffeylabs.org/coffey-labs/stalwart-migrator/issues, and join discussions at community.coffeylabs.org.

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. run will not start until you confirm you have one. See Recovery.

Documentation

📘 Upgrade guide Start here. The whole upgrade, step by step, on docs.ihasmail.org: what to fix first, rehearsing, running it, and what to check afterwards
🧪 Rehearsing Running preflight safely, what rehearse reports, rehearsing on a clone
🐳 Docker Container deployments: flags, what cutover carries and refuses, where the config goes
⚠️ Known Stalwart problems Tenants, the admin account, dropped ACME, certificates on mail ports, the extra recovery boot
🛟 Recovery Why recovery is your snapshot, what the tool keeps, never booting recovery mode again
📊 Status Command and package state, validation, field reports
⚙️ Architecture The design: phases, checkpoints, and the reasoning behind them
🔧 Contributing Building, testing, releases, and where the design is written down

Requirements

  • Linux (amd64 or arm64) with Stalwart 0.15.5, as a systemd service or a single Docker container
  • root on the mail server
  • python3, for Stalwart's own migrate_v016.py
  • stalwart-cli 1.0.2 or later, a separate download from the server
  • an administrator account in Stalwart's directory — not the fallback-admin from config.toml, which does not survive the migration

Fix these on the server first

Both stop a migration, and preflight refuses on both:

  1. Tenants that share a domain. v0.16 requires an account in a tenant to use only that tenant's domains. stalwart-migrate tenants shows who owns what.
  2. 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.

Install

ARCH=amd64   # or arm64
curl -fsSLO https://git.coffeylabs.org/coffey-labs/stalwart-migrator/releases/download/latest/stalwart-migrate-linux-$ARCH.tar.gz
curl -fsSLO https://git.coffeylabs.org/coffey-labs/stalwart-migrator/releases/download/latest/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS
tar -xzf stalwart-migrate-linux-$ARCH.tar.gz
sudo install -m 0755 stalwart-migrate /usr/local/bin/
stalwart-migrate version

Or build from source with Go 1.26.8 or newer: 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.

License

Copyright (C) 2026 Coffey Labs LLC. 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.