# 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 ` 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="https://mail.eternityproject.fi" export STALWART_USER="admin" export STALWART_PASSWORD="" stalwart-cli apply --file accounts-plan.ndjson --dry-run stalwart-cli apply --file accounts-plan.ndjson ``` Windows: ```powershell $env:STALWART_URL = "https://mail.eternityproject.fi" $env:STALWART_USER = "admin" $env:STALWART_PASSWORD = "" stalwart-cli apply --file accounts-plan.ndjson --dry-run stalwart-cli apply --file accounts-plan.ndjson ``` ## 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.