James Thorpe

Dovecot mailbox backups

Feb 05, 2020 dovecot linux restic

Update, 04th Jan 2021

Since writing this up, I discovered that there are more straightfoward solutions to running your own mailserver than manually building and configuring one from scratch. I had a look at 3:

Out of all of them, I liked the look of modoboa the best. However, I'm only ever going to be running a handful of accounts, and simplicity of creating and restoring backups and having the ability to be up and running quickly after any major disaster was high on my list. As such, I went with Mail In a Box - the out of the box experience for dealing with backups (it'll send them straight to S3 itself) seemed better than modoboa. I didn't hand control of DNS to Mail In a Box since the domains I'm using are used for other things too, so it's status page has constant errors, but other than that it's been absolutely fine.

Original Post

I've been using the same provider for my web and email hosting for a number of years. For a variety of reasons (the main one being increases in price vs what I can achieve on AWS), I've been working to move away from them. This website is already hosted on S3 - I don't have any other website needs that aren't met right now. That just leaves my email.

To facilitate this - I wondered "how hard can it be to run a mailserver for a couple of domains and a handful of users?". Well, I now have a mailserver running Postfix and Dovecot, and a handful of plugins - there's plenty of guides around doing this, such as this one. One area I had a hard time figuring out was how to do mailbox backups for all users. I'm using virtual mailboxes, so no actual users on the mailserver.

My goal here was to use restic for backups - I'm using it for other backups already. First thought was to just target the mail storage directory directly with restic to backup, but apparently you shouldn't do that - especially when using the mdbox format - as the indexes may change halfway through and the backup be corrupted. Instead it's safer to use doveadm to sync the mailbox elsewhere, then backup that.

So - first job, backing up all the mailboxes. Seems straightforward enough using doveadm backup, right? It even has an option to iterate all users:

doveadm backup -A maildir:/var/backups/mail/mailboxes/

Nope - when you use -A to iterate over all users, it literally runs the backup for each user one after the other, all of them overwriting the others in the target directory. Ok... so the docs say that the maildir is the same as the mail_location setting. That allows you to use %u, %d and %n as placeholders for user name, domain and name respectively - let's try that:

doveadm backup -A maildir:/var/backups/mail/mailboxes/%u

Nope again! Now you just get a directory called %u, with the same each-user-overwriting-the-previous-one problem (%d and %n have the same issue, before you try). I spent a good amount of time looking around to see if there was a way to get the backup working with -A into separate directories, but just couldn't find one. At this point I figured it was probably best to just script around it.

First up - let's get a list of users:

doveadm user *@*

Works for me - get one user per line in the output. Now to do something useful with it:

doveadm user *@* | while read user; do
  echo Backing up mail for $user
  doveadm backup -u $user maildir:/var/backups/mail/mailboxes/$user
done

Perfect! I also scripted the dumping of the database holding the config for the virtual domains/users, and added the restic backup part after initialising the repository. Here's my complete backup-mail script:

#!/usr/bin/bash
echo Backing up database
rm -f /var/backups/mail/db/dump.sql
mkdir -p /var/backups/mail/db
mysqldump -p<SQL PASSWORD> postfix > /var/backups/mail/db/dump.sql

doveadm user *@* | while read user ; do
  echo Backing up mail for $user
  doveadm backup -u $user maildir:/var/backups/mail/mailboxes/$user
done

export RESTIC_PASSWORD="<RESTIC PASSWORD>"
restic -r s3:s3.amazonaws.com/<MYBUCKET>/restic/mailserverdata backup /var/backups/mail
export RESTIC_PASSWORD="x"

Side note: if anyone has a way to pass in the repository password to restic on the command line rather than having to set it in the environment, I'm all ears...

Back to posts