Like so many people, I use Gmail. There are not a lot of options, and Google Workspace provides me additional useful services and lets me use my own domain name. However there has been a lot of horror stories about users losing access to their Gmail account without any recourse. It is not clear if it can happen with Google Workspace, but the risk is too high to ignore.
I already backup my passwords; now is the time to backup my emails. While Google Workspace offers an export system, I wanted something scriptable. I went with IMAP access since I already use it to read emails with Gnus.
Authentication
Gmail IMAP access requires an application password. You can refer to the Google help center for more information about how to create them.
We will store credentials in the semi-standard
netrc file. For Gmail, add the
following entry to ~/.netrc
:
machine imap.gmail.com port 993 login <email-address> <password>
Make sure that your netrc file is only user readable. Its permissions should
be set to 0600
.
ISync
We will export emails using the synchronization features of
ISync. Note that ISync is the name of the
project while mbsync
is the name of the executable.
The configuration of the program is stored in ~/.mbsyncrc
and is based on
two main concepts: stores and channels. Stores designate location containing
emails; channels describe a link between two stores: the “far” one an
the “near” one.
Let us first define the store for the Gmail account:
IMAPStore gmail
Host imap.gmail.com
Port 993
SSLType IMAPS
User <email-address>
PassCmd "perl -ne '/^machine imap\\.gmail\\.com .*?login <email-address> .*?password (\\S+)/ && print \"$1\\n\"' ~/.netrc"
PipelineDepth 1
While you can use simple use Pass <password>
, we will use PassCmd
to
instruct mbsync
to extract the password from our netrc file. I first tried
to use AWK for this purpose, but it does not support regexp capture, so I went
with Perl. Note that the login, i.e. the email address for Gmail, has to be
properly escaped in the regular expression. For example, bob@example.com
must be included as bob\\@example\\.com
.
PipelineDepth
controls the number of IMAP commands send in a row before
reading responses. It is recommended to use 1
due to Gmail rate limiting.
We then define the local store, i.e. a Maildir directory on our machine:
MaildirStore local
Path ~/mail/
Inbox ~/mail/INBOX
Subfolders Verbatim
Nothing special here, we specify the path of the directory (careful, the final
/
is important), and indicate that IMAP directories are stored in Maildir
directories named the same way. At this point we can create the local
directory:
mkdir -m 700 ~/mail
Finally we define the channel:
Channel backup
Far :gmail:
Near :local:
Patterns *
Create Near
Remove Near
Expunge Near
The Patterns
instruction is used to indicate that we want to synchronize all
IMAP directories. We also indicate that directory creation, directory removal
and mail deletion is only performed on the local
store.
To test that everything is correct, run mbsync -l backup
. If everything is
correct, mbsync
will list all IMAP directories which will be synchronized.
To actually copy all emails, run mbsync --pull backup
. Since I have a
gigabit optic fiber connection, I was only limited by Google servers.
Exporting more than 20 thousands messages (1.5GB) took roughly one hour and a
half.
Systemd timer
At this point, you could simply run mbsync
manually, but I am way too lazy
for that. I use Archlinux, meaning that my init system is the controversial
Systemd. While not everyone likes Systemd, it makes it easy to run periodic
tasks.
First write the service file at ~/.config/systemd/user/mbsync.service
:
[Unit]
Description=Backup emails
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/bin/mbsync --pull backup
[Install]
WantedBy=default.target
Then the timer itself at ~/.config/systemd/user/mbsync.timer
:
[Unit]
Description=Backup emails
[Timer]
OnStartupSec=5min
OnUnitActiveSec=1h
[Install]
WantedBy=timers.target
We can now use systemctl
to enable the service and the timer and execute it
a first time:
systemctl --user enable mbsync.service
systemctl --user enable mbsync.timer
systemctl --user start mbsync
The local Maildir will now be updated 5 minutes after the user session
starts, and every hour after that. You can check the status of the task with
systemctl --user status mbsync
.