Migrate From Self-Hosted Mailcow to Managed JustEmails
Step-by-step migration from self-hosted Mailcow to JustEmails — export, IMAP sync, DNS cutover, and finally deleting that Docker stack.
Step-by-step migration from self-hosted Mailcow to JustEmails — export, IMAP sync, DNS cutover, and finally deleting that Docker stack.
The 3 AM page came on a Sunday. Let's Encrypt renewal had failed silently two weeks earlier, and now Nginx was serving expired certs. Postfix refused to start. Six domains worth of email — bouncing.
I'd been running Mailcow on a $20/month Hetzner VPS for three years. Felt clever about it, honestly. Docker Compose, automatic updates, SOGo webmail. The whole stack. Saved money compared to Google Workspace. Right?
Wrong.
Except I wasn't counting the hours. The monthly maintenance windows. The blacklist checks. The time I spent debugging why Rspamd suddenly decided all mail from .io domains was spam. (Still don't know why. Probably never will.)
That Sunday morning, bleary-eyed and angry, I did the math. Three years of "free" self-hosted email had cost me roughly 150 hours of maintenance. At any reasonable hourly rate, I'd have been better off paying for managed hosting from day one.
This is the migration guide I wish I'd had. Mailcow to JustEmails — step by step, no email lost, and at the end you get to delete that Docker stack forever.
All your historical mail migrated to JustEmails. All your domains receiving new mail through managed infrastructure. SPF, DKIM, and DMARC configured. And a VPS you can finally terminate — no more 3 AM pages, no more Let's Encrypt renewals, no more Docker updates.
JustEmails costs $49/year for unlimited domains and unlimited mailboxes. Your Hetzner or DigitalOcean bill was probably double that monthly. The math is obvious once you write it down — we broke down the true cost of unlimited email hosting in detail.
Before you start, you'll need:
One thing: if you've got mailboxes with 10+ GB of mail, the initial sync will take hours. Start it before bed. imapsync is resumable, so crashes aren't fatal. I learned this the hard way — killed my SSH session halfway through and panicked for ten minutes before discovering it just picks up where it left off.
The other thing to size before you commit is outbound volume, since a managed host meters it and your own Postfix didn't. Sends are capped per account per day — 20/day for the first week, 200/day in the second, 500/day from day 15 on — with mailbox sends and API sends counted against the same number. Warm-up is measured from domain verification rather than signup, so every domain you move over starts at the bottom of a ramp of its own, however old the account is. The imapsync transfer is outside all of it.
Log into JustEmails. Add each domain you're migrating — the dashboard walks you through TXT record verification. Don't change MX records yet. We're not cutting over today.
For each mailbox in Mailcow, create a matching mailbox in JustEmails. Same email address exactly. If you've got aliases and catch-alls, set those up too — JustEmails supports unlimited of both.
Write down the IMAP credentials for each new mailbox. You'll need them for the sync.
Why this matters: The sync tool needs somewhere to push mail. Creating mailboxes first means you can run the bulk sync before touching DNS — no rushed cutover. This is the boring part, but boring is good when email is on the line.
imapsync copies mail between any two IMAP servers. It preserves folder structure, read/unread flags, dates — everything.
On your Mailcow VPS (or any Linux box):
sudo apt update && sudo apt install imapsync
On macOS:
brew install imapsync
Verify it's working:
imapsync --version
You want version 2.x or higher. Older versions have edge cases with folder naming that'll bite you.
Here's the command for a single mailbox. Replace the placeholders:
imapsync \
--host1 mail.yourmailcowserver.com --port1 993 --ssl1 \
--user1 "alex@yourcompany.com" --password1 "mailcow-password" \
--host2 mail.justemails.app --port2 993 --ssl2 \
--user2 "alex@yourcompany.com" --password2 "justemails-password" \
--automap
The --automap flag handles folder name translation. Mailcow uses standard IMAP folder names, so this should be clean — unlike Gmail migrations where labels get weird.
Expected output: You'll see progress bars for each folder. A 5 GB mailbox takes 2-4 hours depending on your connection. Let it run.
For multiple mailboxes, loop through a CSV:
while IFS=, read -r user mcpass jepass; do
imapsync \
--host1 mail.yourmailcowserver.com --port1 993 --ssl1 \
--user1 "$user" --password1 "$mcpass" \
--host2 mail.justemails.app --port2 993 --ssl2 \
--user2 "$user" --password2 "$jepass" \
--automap
done < users.csv
The CSV has three columns: email, mailcow password, justemails password. Store it somewhere safe and delete it after migration.
This is the step people skip and then regret.
Your MX records probably have a TTL of 3600 (1 hour) or higher. Some registrars default to 86400 (24 hours). That means after you change MX records, some mail servers will keep sending to Mailcow for up to a day.
In your DNS panel:
Now wait 24 hours. Yes, really. The old TTL needs to expire globally before the new short TTL takes effect. I know you want to skip ahead. Don't. I've seen migrations go sideways because someone got impatient at hour six. We covered why self-hosted email is risky — rushing DNS is how you validate those risks.
While you wait, grab the JustEmails DNS records from your dashboard: MX, SPF include, DKIM CNAMEs. You'll add these tomorrow.
Deep breath. Coffee in hand. This is the moment. (I won't lie — I still get a little nervous doing DNS cutovers, even after years of this.)
Delete your old MX records pointing to your Mailcow server:
mail.yourmailcowserver.com (priority 10)
Add the JustEmails MX record — a single host, the same shape as the one you just deleted:
mail1.justemails.app (priority 10)
(Check your JustEmails dashboard for the exact value.)
Your current SPF probably looks like:
v=spf1 ip4:YOUR.VPS.IP.ADDRESS ~all
Or maybe:
v=spf1 a mx ~all
Change it to:
v=spf1 a:mail1.justemails.app ~all
The a: mechanism authorises whatever IP mail1.justemails.app resolves to — that replaces both the hard-coded VPS IP you used to publish and the a mx shorthand, neither of which points anywhere useful once mail leaves your server. Keep any other includes you need (marketing tools, transactional email services). One SPF record only — and watch for SPF permerror from too many includes if you have complex setups.
JustEmails generates DKIM keys automatically. Add the CNAME records from your dashboard — usually two:
je1._domainkey.yourcompany.com → je1.dkim.justemails.app
je2._domainkey.yourcompany.com → je2.dkim.justemails.app
You can delete your old Mailcow DKIM records (dkim._domainkey). They're dead keys now.
If you're at p=reject, consider dropping to p=quarantine for a week during the transition. Watch your DMARC aggregate reports for alignment failures. Our guide on ramping DMARC from none to reject safely covers the details.
Use MXToolbox to confirm:
https://mxtoolbox.com/SuperTool.aspx?action=mx:yourcompany.com
You should see the single JustEmails MX host. Check SPF and DKIM too. If you still see your Mailcow server, wait longer — DNS propagation isn't instant despite what anyone tells you.
Send a test email from an external account. Verify it arrives in JustEmails, not Mailcow.
Mail that arrived at Mailcow between your initial sync and the MX cutover is still sitting on your VPS. Run imapsync again:
imapsync \
--host1 mail.yourmailcowserver.com --port1 993 --ssl1 \
--user1 "alex@yourcompany.com" --password1 "mailcow-password" \
--host2 mail.justemails.app --port2 993 --ssl2 \
--user2 "alex@yourcompany.com" --password2 "justemails-password" \
--automap
Same command as before. imapsync only copies new or changed messages — it won't duplicate what's already synced. This delta pass is usually fast. Minutes, not hours.
Run it for every mailbox.
Your users need new server settings. Send them this:
Incoming (IMAP):
Outgoing (SMTP):
Outlook, Apple Mail, Thunderbird, mobile apps — all of them need updating. Budget 10 minutes per user for questions. More if someone's still using Outlook 2016. (Why are they still using Outlook 2016? Don't ask.) If you're managing analytics for these same domains, JustAnalytics follows a similar flat-fee model — consolidate where it makes sense.
Keep Mailcow running for 48-72 hours after cutover. Some enterprise mail servers cache MX records aggressively. Check the Mailcow admin panel — if new mail is still arriving there, DNS hasn't fully propagated.
Run one more delta sync at the 48-hour mark.
Once you've confirmed zero new mail is arriving at Mailcow for 24 hours straight:
cd /opt/mailcow-dockerized
docker compose down
Keep the VPS around for 30 days in case you need to pull anything. Storage is cheap. After that — finally — you can delete the VPS.
Cancel that $20/month Hetzner bill. You're done being your own email ops team.
Honestly? The relief is real. Three years of low-grade anxiety about whether the mail server was up — gone.
imapsync fails with "NO LOGIN"
Wrong IMAP password. In Mailcow, IMAP uses the same password as webmail login. Double-check you're using the actual user password, not an app password or admin credential. Review our guide on email ports 25, 465, 587, 993 explained if you're hitting connection issues.
Mail still arriving at Mailcow after MX cutover
DNS propagation isn't complete. Check your old MX record TTL — if it was 86400 before you lowered it, some servers may cache the old value for another day. Keep Mailcow running and run delta syncs.
DKIM fails in testing
CNAME records need 15-60 minutes to propagate. If still failing after an hour, check the record names — the underscore in _domainkey matters. It's je1._domainkey, not je1.domainkey.
Folder structure looks different after sync
Mailcow and JustEmails both use standard IMAP folders, so this usually isn't an issue. If you had custom folders with unusual characters, imapsync may have renamed them. Manual fix: drag and drop in your mail client.
SOGo calendar and contacts missing
JustEmails is email-only — no calendar or contacts sync. If you were using SOGo for calendar, you'll need a separate solution. Most people migrate to Google Calendar (free) or a dedicated CalDAV provider.
Look, I get it — losing SOGo stings if you were actually using it. But be honest: were you? Most Mailcow users I've talked to set up SOGo once, used it for a week, and went back to Google Calendar anyway. Email hosting shouldn't cost extra because of bundled features you might not use.
You've escaped the maintenance treadmill. No more Docker updates, no more Rspamd tuning, no more Let's Encrypt renewals at 3 AM.
If you're managing multiple product domains — SaaS founders, agencies — check out our multi-domain email management best practices. The patterns that made Mailcow painful (per-server config for each domain) don't apply when everything's managed from one dashboard.
For the analytics side of your stack, JustAnalytics follows the same flat-fee philosophy — no per-seat billing, just useful data. And if you're running paid campaigns, ClickzProtect catches the click fraud that eats ad budgets.
Questions about migration edge cases? support@justemails.app. We answer email. (That part's easy now — and yes, I'm aware of the irony.)
Plan for 3-4 days total. Day 1 is account setup and initial IMAP sync. Day 2 is DNS preparation (lowering TTL). Day 3 is MX cutover and delta sync. Day 4 is verification and Mailcow shutdown. Actual hands-on time is about 2-3 hours spread across those days — the rest is waiting for DNS propagation and running sync jobs.
Not if you keep Mailcow running during the transition. Run imapsync before the MX cutover (bulk sync), then again after (delta sync to catch stragglers). Keep Mailcow accepting mail until DNS fully propagates — usually 24-48 hours. Only shut down the Docker stack after you've confirmed zero new mail is arriving there.
Yes. Your SPF record currently includes your VPS IP or Mailcow's SPF. Swap it to a:mail1.justemails.app — JustEmails publishes no SPF include, so you authorise the sending host directly with an a: mechanism. DKIM keys are server-specific — Mailcow generated keys that live on your VPS, so you'll publish new CNAME records pointing to JustEmails' DKIM infrastructure. JustEmails auto-generates these; you just copy-paste the DNS records.
Once you've verified all mail is flowing through JustEmails and run your final delta sync, you can shut down the Docker stack. Keep the VPS around for 30 days in case you need to pull any missed mail — storage is cheap, peace of mind is priceless. After that, delete the VPS and stop paying $10-25/month for self-hosted infrastructure.
Unlimited custom domain email hosting for $49/year flat — unlimited domains, unlimited mailboxes, 10 GB storage, full IMAP/SMTP. Built for agencies, freelancers, and anyone managing email across more than one domain.