5.8 KiB
Mailbox migration: docker-mailserver → Stalwart
Tools to move accounts, aliases, and mail from a docker-mailserver instance into
this Stalwart deployment.
Overview
| Step | Linux (bash) | Windows (PowerShell) | Purpose |
|---|---|---|---|
| 1 | export-docker-mailserver.sh |
(run on the Linux docker-mailserver host) | Dumps accounts + aliases from docker-mailserver's config files into accounts.csv |
| 2 | (manual) | (manual) | Fill in new_password (and old_password if not already known) in accounts.csv |
| 3 | generate-account-plan.sh |
New-AccountPlan.ps1 |
Turns accounts.csv into a stalwart-cli apply NDJSON plan that creates the domain, accounts, and aliases in Stalwart |
| 4 | stalwart-cli apply |
stalwart-cli apply |
Applies the generated plan to Stalwart |
| 5 | sync-mailboxes.sh |
Sync-Mailboxes.ps1 |
Runs imapsync (via Docker) for every account, copying all folders/messages from docker-mailserver to Stalwart |
| 6 | (manual) | (manual) | Verify, then repoint MX/DNS and decommission docker-mailserver |
Both old and new mail servers run Linux, so the bash scripts are the primary path;
the PowerShell scripts are equivalent for driving the migration from a Windows
workstation. Make the bash scripts executable once: chmod +x migration/*.sh.
generate-account-plan.sh requires jq (apt install jq / dnf install jq).
docker-mailserver stores passwords as one-way hashes (config/postfix-accounts.cf),
so they cannot be reused directly in Stalwart. Each account gets a new password
during migration; old_password is only needed temporarily so imapsync can log in
to the source server to copy mail.
1. Export accounts and aliases from docker-mailserver
Run on the docker-mailserver host (or docker exec into its container), pointing at
its config directory:
./export-docker-mailserver.sh /path/to/docker-mailserver/config > accounts.csv
This produces accounts.csv with columns email,new_password,old_password,aliases,quota_mb,
new_password and old_password left blank for you to fill in.
2. Fill in passwords
Edit accounts.csv:
new_password: the password the account will have on Stalwart. Generate strong random values; users can change them later via the WebUI.old_password: the account's current, plaintext docker-mailserver password, needed only soimapsynccan authenticate to the source during copy. If you don't have it, reset it temporarily on docker-mailserver withsetup email update <email> <temp-password>and record that value here.aliases: optional,;-separated list of extra local-parts on the same domain that should deliver to this mailbox (e.g.info;sales).quota_mb: optional mailbox quota in MB (blank = unlimited).
Never commit a filled-in accounts.csv; it contains plaintext passwords.
3. Generate the Stalwart account plan
Linux:
./generate-account-plan.sh -c accounts.csv -d eternityproject.fi -o accounts-plan.ndjson
Windows:
./New-AccountPlan.ps1 -CsvPath accounts.csv -Domain eternityproject.fi -OutFile accounts-plan.ndjson
This upserts the Domain, one Account/User per row (with a Password credential
set to new_password and any aliases), so re-running after edits is safe.
4. Apply the plan to Stalwart
Linux:
export STALWART_URL="http://127.0.0.1:8081"
export STALWART_USER="admin"
export STALWART_PASSWORD="<admin password>"
stalwart-cli apply --file accounts-plan.ndjson --dry-run
stalwart-cli apply --file accounts-plan.ndjson
Windows:
$env:STALWART_URL = "http://localhost:8081"
$env:STALWART_USER = "admin"
$env:STALWART_PASSWORD = "<admin password>"
stalwart-cli apply --file accounts-plan.ndjson --dry-run
stalwart-cli apply --file accounts-plan.ndjson
Use the permanent administrator username and password created in the Stalwart setup UI, not a mailbox login. The default Compose deployment binds the administration API only to the local host, so https://mail.eternityproject.fi is not the CLI endpoint unless you have separately configured a reverse proxy for it. Confirm the CLI can reach and authenticate to the local API before applying a real plan:
stalwart-cli apply --url http://localhost:8081 --user admin --file accounts-plan.ndjson --dry-run
The command prompts for the password when STALWART_PASSWORD and --password are omitted. If the command says authentication failed, reset or use the correct permanent administrator password in the Stalwart WebUI. If it says failed to connect, verify docker compose ps stalwart and use http://localhost:8081 on Docker Desktop or http://127.0.0.1:8081 directly on the Linux server.
5. Copy mail with imapsync
The sync scripts use the upstream gilleslamiral/imapsync Docker image. The first
run pulls the image automatically, so Docker Hub access is required.
Linux:
./sync-mailboxes.sh -c accounts.csv -s mail.old-domain.example -t mail.eternityproject.fi -n
Windows:
./Sync-Mailboxes.ps1 `
-CsvPath accounts.csv `
-SourceHost mail.old-domain.example `
-DestHost mail.eternityproject.fi `
-DryRun
(-n / -DryRun runs imapsync in dry-run mode first; drop it to actually copy messages.)
Review the dry-run output, then re-run without -DryRun to actually copy messages.
The script is safe to re-run: imapsync skips messages that already exist at the
destination.
6. Cut over
- Spot-check folder counts/message counts on a few mailboxes in the Stalwart WebUI.
- Run
Sync-Mailboxes.ps1once more shortly before cutover to catch mail received during the migration window. - Update MX records to point at
mail.eternityproject.fi(see the top-level README.md). - Once confirmed, decommission docker-mailserver and rotate the temporary
old_passwordvalues you set in step 2.