I put together an automatic hourly backup of selected directories using rsync and a systemd timer. It was a little bit more of a hassle than I anticipated, given my complete unfamiliarity with systemd, so I thought I’d document it for my own records and in case it’s useful to someone else on a distribution with systemd who has a similar use-case to mine.
Why a systemd timer and not cron?
In my case, mostly because I already have systemd and the timer comes with it, while I’d have to install cron. Evidently a systemd timer also has some advantages over cron such as easy debugging and flexibility. The Arch Wiki lists some benefits and caveats of using the systemd timer as a cron replacement.
Step 1: Set up the backup script
I used rsync for the backup script. While I’m not ssh-ing into an external machine and just using an external hard drive, it’s simple yet robust enough for this use-case, too. My backup script file is just a shell script that starts with lines like:
As you may know if you have some familiarity with rsync, the trailing slash on the source is significant.
Without the trailing slash, the files in the source directory are copied into a subdirectory of the destination.
This means that, in the above example, files and diretories from the
Pictures directories are backed up to the
Pictures directories of the external drive.
The files and directories in
.config, on the other hand, are backed up to
Programs/.config in the external drive.
This file was made executable with
chmod +x and lives in my
~/.local/bin with the file name
Yeah, I guess it should be
backup.sh, but without the extension it’s shorter to run manually from the command line and it works.
I am told the more proper way to do this is by creating a symlink named
backup to my actual .sh file, but I don’t have a separate coding or development directory and I’m lazy lol.
Oh, and of course, to call backup in
~/.local/bin directly from the command line I had to add
~/.local/bin to my
$PATH, which I did with a line in
.bashrc like this:
~/go/bin ahead of
$PATH rather than appending them, btw, because this way my local executables take precedence over the system ones.
If there happens to be a binary called
backup in the system files, for instance, when I type
backup in the terminal I’ll be getting the
backup script I wrote.
This was another piece of advice from my dev friend, one that I actually took.
Once the backup script is tested and confirmed to be working, it’s time to set up the systemd service and timer.
Step 2: Set up the systemd service and timer
I wrote a
filebackup.service text file that consists of just the following lines:
[Unit] Description=File backup service [Service] ExecStart=/home/ljwrites/.local/bin/backup
You can set the description to whatever text string you like, and
ExecStart is obviously a call to execute the
backup file I put in
And here’s my
[Unit] Description=Hourly backup of files [Timer] OnCalendar=Hourly AccuracySec=1s Persistent=false Unit=filebackup.service [Install] WantedBy=timers.target
OnCalendar=Hourly sets the hourly interval, and
Unit calls the
filebackup.service from above.
I have no idea if
timers.target is the best timer for this but it works.
There’s a more detailed explanation of timer types on (where else?) Stack Exchange, so check up on that if you’re interested.
According to that explanation it looks like basic.target would have worked, too.
I moved both the service and timer files to the
Step 3: Activate the systemd timer
You can check the list of timers with:
You can include inactive timers with the
systemctl list-timers --all
filebackup.timer shouldn’t be on the list yet.
You can add it with:
sudo systemctl enable --now filebackup.timer
Check timer list again and it should be there, with the NEXT and LEFT columns set to the start of the next hour.
Update 2021/6/15: If the NEXT and LEFT columns of the backup timer show up as n/a, it may help to restart it using the following command:
sudo systemctl restart filebackup.timer
Step 4: Check if the backup ran with systemd journal
You can check if the backup timer is running on schedule with:
sudo journalctl -u filebackup.*
I’ve set this to an alias because I like the peace of mind in knowing I have regular backups of my stuff.
And that’s it!
There you have it, my file backup setup. It’s really simple and basic, but does what I need it to do. It also took me a while to get working properly because I couldn’t find specific step-by-step instructions for my particular needs. Maybe it’ll help others who have similar use-cases but are not devs.