Files
2026-09-18 12:43:20 +03:00

139 lines
5.8 KiB
Markdown

# 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:
```bash
./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 so `imapsync` can authenticate to the source during copy. If you don't
have it, reset it temporarily on docker-mailserver with
`setup 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:
```bash
./generate-account-plan.sh -c accounts.csv -d eternityproject.fi -o accounts-plan.ndjson
```
Windows:
```powershell
./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:
```bash
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:
```powershell
$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:
```powershell
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:
```bash
./sync-mailboxes.sh -c accounts.csv -s mail.old-domain.example -t mail.eternityproject.fi -n
```
Windows:
```powershell
./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.ps1` once 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](../README.md)).
- Once confirmed, decommission docker-mailserver and rotate the temporary
`old_password` values you set in step 2.