Migrating Email from Google Workspace to Outlook.com
By Adrian Sutton
For many years I’ve been using the free tier of Google Workspace (previously Google Apps for Your Domain) to host my email. While I almost never use the actual web interface itself having google handle the IMAP hosting and spam filtering was an awful lot easier than hosting it myself. Of course Google are now ending their free tier and requiring all users to pay – fair enough, but the cost per user is just too much for me to justify it.
It turns out though that as part of my Office 365 subscription to get access to office apps, we also get Outlook.com premium which provides quite suitable email hosting for what I need. While it can, at least theoretically, use a custom domain directly, that domain has to be hosted with GoDaddy and the transfer process seemed hard to do without downtime and migration issues. The family would not be forgiving of that. So instead, since my domains are already hosted with CloudFlare we’ll just use their email forwarding so emails for our domain get forwarded to Outlook.com and Outlook.com happily lets us send with the custom domain as the From address so it all works out. Switching over will just mean setting up the right forwards in CloudFlare and pressing the button to switch the MX records from Google to CloudFlare.
Except there’s a whole heap of email in Google’s IMAP store that needs to be moved over to Outlook’s IMAP store. That could be done by simply adding both accounts to an email application and dragging the emails over. For small numbers of emails that works pretty well, but it’s pretty easy for things to get interrupted part way through and some emails are copied over and others aren’t and then it’s hard to restart the copy without getting duplicates etc. Pretty quickly it becomes unworkable for a large number of emails.
Instead, I’ve setup isync (actual command name mbsync) to do the transfer. It appears to be very good at resuming after an interruption and just picking up where it left off copying folders and emails. It even gets things like the read/unread status right. The configuration isn’t entirely straight forward so for anyone else doing this migration, my config is below.
It’s setup with two users but more can be added by following the same pattern. You can either just run mbsync -a
to migrate all email for all users or mbsync user1
to migrate just email for user1 and similarly for user2. Migrating them all in one command does them both sequentially so generally it will be faster to migrate each user separately.
Often, mbsync will exit for a variety of reasons (gmail suddenly closing the connection, outlook.com enforcing request limits etc) so I run it with a simple bash for loop:
while true; do mbsync adrian; echo "Disconnected, reconnecting after delay..."; sleep 30; done
Note that it will loop forever so you’ll want to check it from time to time and when it’s reporting that nothing needed to be copied just ctrl-C it to exit.
IMAP is not a particularly efficient protocol and rate limiting winds up being applied so this is definitely not a fast process, but you can just set it off and leave it run in the background until it’s done.
IMAPAccount gmail-user1
Host imap.gmail.com
User
# Simplest approach is to enable 2FA and generate an app specific password to use here.
# Revoke the app specific password again once the transfer is complete
Pass
Timeout 120
AuthMechs LOGIN
SSLType IMAPS
IMAPStore user1-source
Account gmail-user1
IMAPAccount outlook-user1
Host imap-mail.outlook.com
User
# Simplest approach is to enable 2FA and generate an app specific password to use here.
# Revoke the app specific password again once the transfer is complete
Pass
SSLType IMAPS
Timeout 120
IMAPStore user1-target
Account outlook-user1
IMAPAccount gmail-user2
Host imap.gmail.com
User
Pass
Timeout 120
AuthMechs LOGIN
SSLType IMAPS
IMAPStore user2-source
Account gmail-user2
IMAPAccount outlook-user2
Host imap-mail.outlook.com
User
Pass
SSLType IMAPS
IMAPStore user2-target
Account outlook-user2
Channel user1-main
Far :user1-source:
Near :user1-target:
Patterns * ![Gmail]* !Archive
Create Near
Expunge Near
CopyArrivalDate yes
Sync Pull
SyncState *
SyncState ~/.mail/imap-transfer/user1/
Channel user1-sent
Far :user1-source:"[Gmail]/Sent Mail"
Near :user1-target:"Sent"
Create Near
Expunge Near
Sync Pull
CopyArrivalDate yes
SyncState *
SyncState ~/.mail/imap-transfer/user1/
Channel user1-trash
Far :user1-source:"[Gmail]/Trash"
Near :user1-target:"Deleted"
Create Near
Expunge Near
Sync Pull
CopyArrivalDate yes
SyncState *
SyncState ~/.mail/imap-transfer/user1/
Group user1
Channel user1-main
Channel user1-sent
Channel user1-trash
Channel user2-main
Far :user2-source:
Near :user2-target:
Patterns * ![Gmail]* !Archive
Create Near
Expunge Near
CopyArrivalDate yes
Sync Pull
SyncState *
SyncState ~/.mail/imap-transfer/user2/
Channel user2-sent
Far :user2-source:"[Gmail]/Sent Mail"
Near :user2-target:"Sent"
Create Near
Expunge Near
Sync Pull
CopyArrivalDate yes
SyncState *
SyncState ~/.mail/imap-transfer/user2/
Channel user2-trash
Far :user2-source:"[Gmail]/Trash"
Near :user2-target:"Deleted"
Create Near
Expunge Near
Sync Pull
CopyArrivalDate yes
SyncState *
SyncState ~/.mail/imap-transfer/user2/
Group user2
Channel user2-main
Channel user2-sent
Channel user2-trash