Migrating email between IMAP accounts

| 3 minute read

I’ve been using Fastmail as my email provider since I first started migrating away from Gmail back in 2015. Its been a fantastic service that I haven’t ever regretted paying for.

When I started out they had an IMAP importer that let you pick and choose which folders you wanted to import. It was great since it let me migrate my email in bite sized chunks as I slowly changed my email on services as I went.

Sadly, that importer has been replaced with a new one that only lets you import your whole IMAP account along with completely removing the old one. Support suggested I limit which folders I expose via IMAP through Gmail as a way of replicating the old importer but I found that cumbersome and awkward to do. Time to homebrew it.

IMAPFilter is a fantastic email filtering utility I’ve used in the past for when Gmail’s inbuilt features didn’t cut it. Along with filtering you can outright copy or move messages between IMAP accounts which is exactly what I’m after. All you need to do is configure the two IMAP accounts, work out what folder you want to migrate and select the emails in it, then copy the messages to the new account.

Turns out it can be done in ~19 lines of IMAPFilter code, check it out below. It wont delete any emails but just copies them. All original email data (such as headers) are kept intact and the date it was delivered doesn’t change. No need to worry about the emails going out of order.

-- The time in seconds for the program to wait for a mail server's response (default 60)
options.timeout = 120

-- According to the IMAP specification, when trying to write a message to a non-existent mailbox, the server must send a hint to the client, whether it should create the mailbox and try again or not. However some IMAP servers don't follow the specification and don't send the correct response code to the client. By enabling this option the client tries to create the mailbox, despite of the server's response.
options.create = true

-- By enabling this option new mailboxes that were automatically created, get also subscribed; they are set active in order for IMAP clients to recognize them
options.subscribe = true

-- Normally, messages are marked for deletion and are actually deleted when the mailbox is closed. When this option is enabled, messages are expunged immediately after being marked deleted.
options.expunge = true

gmail = IMAP {
  server   = "mail.gmail.com",
  username = "example@gmail.com",
  password = "examplePassword",
  ssl      = "tls1"
}

fastmail = IMAP {
  server   = "imap.fastmail.com",
  username = "example@fastmail.com",
  password = "examplePassword",
  ssl      = "tls1"
}

src_folder = 'Shopping'
dest_folder = 'Shopping'

emails = gmail[src_folder]:select_all()

-- Uncomment to check the Subject line of emails you're going to migrate
--for _, message in ipairs(emails) do
--	mailbox, uid = unpack(message)
--	subject = mailbox[uid]:fetch_field('Subject')
--	print(subject)
--end

emails:copy_messages(fastmail[dest_folder])

If you want to delete the emails instead of copy them, change copy_messages to move_messages. Enjoy!