Digital Adventures

ArchLinux, Ruby, Rails, OpenSource

Schedule Automatic Backups With Systemd

Backups can be a real lifesaver and the lack of them can make your life really miserable. I noticed this after my latest btrfs-disaster (yeah.. i switched back to ext4 for now). After this episode which involved some hackish btrfs restore usage I got really serious about backups.

I hear many people rant about systemd and while I can understand some concerns others might have with it I think it solves real problems and can do some pretty amazing stuff. This blog post should give you an overview of how to use some of these features to schedule automated hassle-free backups.

Overview

My goal was to be able to schedule backups to be run in specific intervals whenever the backup drive is connected to my notebook. Some more specific requirements were the following:

  1. Ensure a backup is run as soon as possible if

    1. the system wasn’t running at the time the backup was scheduled
    2. the drive wasn’t connected at the time the backup was scheduled
  2. Ensure backups are retried if they fail

fstab and Automount

First of all the backup drive needs to be recognized and automatically mounted after it’s plugged in. I formatted the drive and gave it a label backups. I can use this label to identify the drive and it should always be preferred over device names such as sdaX. Another option would be to use an UUID.

I added the following entry to /etc/fstab:

1
LABEL=backups   /media/backups  ext4    noauto,rw,relatime,data=ordered

Adding the noauto-option here is important, otherwise the system won’t boot if the device is not found.

For automatically mounting the partition with the label backups systemd comes into play:

Create a wants-directory in /etc/systemd/system:

1
2
export SYSTEMD_ESCAPED_DEVICE=$(systemd-escape --suffix device dev/disk/by-label/backups)
mkdir /etc/systemd/system/${SYSTEMD_ESCAPED_DEVICE}.wants

Symlink media-backups.mount to that directory:

1
2
ln -s /run/systemd/generator/media-backups.mount \
      /etc/systemd/system/${SYSTEMD_ESCAPED_DEVICE}.wants

So what does this actually mean? All units in a wants-directory are started as soon as the unit named in the directory is started successfully. This means that as soon as the device /dev/disk/by-label/backups appears media-backups.mount is run so that the device will be mounted at /media/backups.

Systemd timers

Timers are another awesome feature of systemd and they can be used as a replacement for cron. Each timer unit needs a corresponding unit which will be triggered when the timer is due. Timers can also be written as template units like in my following example timer:

(schedule-daily-backup@.timer) download
1
2
3
4
5
6
7
8
9
10
[Unit]
Description=timer for daily backup of %i

[Timer]
OnCalendar=daily
Persistent=true
Unit=schedule-backup@%i.service

[Install]
WantedBy=default.target

The option Persistent=true ensures that the timer is run as soon as possible after being due if the system was shut down at that time. Unit defines the systemd unit that should be run when the timer is due. %i is substituted with whatever is used after the @ in the unit name i.e. systemctl start schedule-daily-backup@home.timer results in %i having the value home.

I placed the timer into $XDG_CONFIG_HOME/systemd/user/ and enabled and started it with systemd --user.

Scheduling the backup

Let’s take a look at schedule-backup@.service.

(schedule-backup@.service) download
1
2
3
4
5
6
7
[Unit]
Description=schedule of a backup of %i

[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl --user enable run-backup@%i
ExecStartPost=/usr/bin/systemctl --user start run-backup@%i

This too is placed into $XDG_CONFIG_HOME/systemd/user/

This service not only enables the service which starts the backup but also tries running it right away in ExecStartPost.

Wiring it all up

Finally let’s take a look at run-backup@.service.

(run-backup@.service) download
1
2
3
4
5
6
7
8
9
10
[Unit]
Description=backup of %i

[Service]
Type=oneshot
ExecStart=/home/jokke/bin/backup %i
ExecStartPost=/usr/bin/systemctl --user disable run-backup@%i

[Install]
WantedBy=media-backups.mount

This service is responsible for starting the actual backup script. In my case it receives the name of the backup as a parameter and runs it. There are two things to notice here:

  1. The Install section defines WantedBy=media-backups.mount. Sound familiar? This is the Unit that is run when the device is plugged in. So when ever the device is plugged in it will be mounted at /media/backups which in turn triggers all Units in media-backups.mount.wants directories. schedule-backup@.service runs systemctl --user enable on this service and by doing so creates a symlink in this exact wants-directory.
  2. ExecStartPost is set to disable the service when the command in ExecStart is run successfully.

To start scheduling a daily backup for home all I need to do is:

systemctl --user start schedule-daily-backup@home.timer (and enable it)

When the timer is due it runs schedule-backup@home.service which enables run-backup@home.service and tries to run it right away. If the drive wasn’t plugged in at this time the backup will be run as soon as it’s attached again.

Comments