I recently blogged about my e-mail backup strategy, using imap-backup
. However I’ve found in real life this tool trips up quite a lot, and doesn’t really sync very satisfactorily on an ongoing basis.
It’s also been slightly nagging at me that I’m just mirroring – so if an e-mail is deleted it’s gone for good once the next backup has run. But equally I didn’t want to have to backup the whole shebang every time.
So, the solution I’ve reached is a combination of offlineimap and my old friend restic to do incremental backups securely and efficiently. The restic repository also goes up to good old sync.com, which is potentially a bit of duplication – but I wanted the snapshot capability (rather than individual file histories).
Basic workflow is
- Use offlineimap to mirror the imap server into local maildir folders
- Use restic to take a snapshot of the maildir folders
Of course, this is all done using Docker containers. So step one uses a volume within the docker container, step 2 uses a local filesystem mount for the restic repository.
Dockerfile
Pretty standard – install offlineimap
and restic
, then copy across the config file to ~/.offlineimaprc
. Worth saying that ~/.offlineimaprc
has passwords in it, so this image shouldn’t be published anywhere!
FROM ubuntu:latest AS build
ENV HOME /root
SHELL ["/bin/bash", "-c"]
RUN apt-get update && apt-get -y --no-install-recommends install \
offlineimap ca-certificates restic
RUN update-ca-certificates
RUN adduser --system --uid 1000 --home /home/imapuser --shell /bin/bash imapuser
RUN mkdir /imap-backup && chown imapuser /imap-backup
USER imapuser
ENV HOME /home/imapuser
WORKDIR /home/imapuser
COPY --chown=imapuser offlineimap.conf .offlineimaprc
RUN chmod 0600 .offlineimaprc
CMD ["offlineimap"]
.offlineimaprc
Best off reading the docs, but this is what I had to do to get it working nicely. Looking at it again now, the windows stuff and nametrans may no longer be required, as I originally used a mounted windows volume rather than a native docker volume for this step.
Of course you can add as many accounts as you want.
[general]
accounts = account1,account2
metadata = /imap-backup/meta
ui = basic
fsync = False
[DEFAULT]
remotehost = imap.somewhere.com
starttls = yes
ssl = yes
sslcacertfile = OS-DEFAULT
expunge = no
maildir-windows-compatible = yes
nametrans = lambda folder: re.sub('[ \':/]', '_', folder)
[Account account1]
localrepository = Account1Local
remoterepository = Account1Remote
[Repository Account1Local]
type = Maildir
localfolders = /imap-backup/account1_eutony_net
[Repository Account1Remote]
type = IMAP
readonly = True
remoteuser =
[email protected]
remotepass = account1password
[Account account2]
localrepository = Account2Local
remoterepository = Account2Remote
[Repository Account2Local]
type = Maildir
localfolders = /imap-backup/account2_eutony_net
[Repository Account2Remote]
type = IMAP
readonly = True
remoteuser =
[email protected]
remotepass = account2password
Backup.bat
This is slightly more involved. I’ve got the best outcome when I sync each account individually. This script spins up a docker container for each account in turn, with a named volume mount called offlineimap
which stores the local maildir (so it persists between runs). Then it spins up a final docker container to do the restic business.
That last line is a bit more involved it has to mount two volumes – the offlineimap
as above, but then a Windows folder on my host PC mounted at /restic
which stores the restic repository. This Windows folder is one managed by sync.com. As you can see, I needed to add some extra bits and pieces to the restic command to get it to do the incremental backup properly with a mounted windows volume.
docker container run --rm -v "offlineimap:/imap-backup" ^
offlineimap offlineimap -q -a account1
docker container run --rm -v "offlineimap:/imap-backup" ^
offlineimap offlineimap -q -a account2
docker container run -e RESTIC_PASSWORD=****** --rm -v "offlineimap:/imap-backup" ^
-v "C:/Users/james/Backups/email/offlineimap/restic:/restic" ^
offlineimap restic --ignore-inode --cache-dir=/imap-backup/.cache ^
--host=offlineimap -r /restic backup /imap-backup
In action
The first run is slow, as it has to download entire mailboxes, but thereafter it’s only a few seconds per account.
The abridged log output is shown below. The numbers won’t add up as this is from one of my actual runs, which syncs 6 or 7 e-mail accounts.
OfflineIMAP 8.0.0
Licensed under the GNU GPL v2 or any later version (with an OpenSSL exception)
imaplib2 v3.05, Python v3.10.6, OpenSSL 3.0.2 15 Mar 2022
*** Processing account account1
Establishing connection to imap.somewhere.com:993 (Account1Remote)
Syncing Drafts: IMAP -> Maildir
Skipping Drafts (not changed)
Syncing Sent Items: IMAP -> Maildir
Skipping Sent Items (not changed)
Syncing Spam: IMAP -> Maildir
Skipping Spam (not changed)
Syncing INBOX: IMAP -> Maildir
Copy message UID xxxx (1/2) Account1Remote:INBOX ->Account1Local:INBOX
Copy message UID xxxx (2/2) Account1Remote:INBOX ->Account1Local:INBOX
*** Finished account 'account1' in 0:06
OfflineIMAP 8.0.0
Licensed under the GNU GPL v2 or any later version (with an OpenSSL exception)
imaplib2 v3.05, Python v3.10.6, OpenSSL 3.0.2 15 Mar 2022
*** Processing account account2
[...]
*** Finished account 'account1' in 0:01
using parent snapshot 25d4e6dd
Files: 68 new, 9 changed, 39759 unmodified
Dirs: 0 new, 49 changed, 1464 unmodified
Added to the repo: 5.871 MiB
processed 39836 files, 3.893 GiB in 0:02
snapshot ebd8f443 saved