5

summary:

I am trying to set up a systemd timer to regularly backup a directory using the rsync command. I made an rsync command that works when run manually in a terminal, but it doesn't work correctly when run as a systemd timer.

detailed explanation:

As a simple example, I have following directory tree in /home/trevor/test_dir/:

dir_a/
   file_a.png
dir_b/
   file_b.png
dir_c/
   file_c.png

I want to use rsync to copy this directory to my SSD for backup (mounted at /my_ssd/). But I want to exclude the directories dir_a and dir_b. So I run the command

rsync -a --exclude={/dir_a,/dir_b} /home/trevor/test_dir/ /my_ssd/test_dir/

This command works when I run it from a terminal: it excludes the directories dir_a and dir_b, but keeps dir_c.

Next, I try to make a systemd service and timer to run that command routinely (note that I determined the following steps mostly from the systemd/Timers page on the Arch Linux Wiki). I make the following service file

/etc/systemd/system/backup_test.service:

[Unit]
Description=systemd backup test

[Service]
Type=simple
ExecStart=rsync -a --exclude={/dir_a,/dir_b} /home/trevor/test_dir/ /my_ssd/test_dir/

and the following timer file which will run the command every 30 seconds (for example)

/etc/systemd/system/backup_test.timer:

[Unit]
Description=systemd backup test timer

[Timer]
OnCalendar=*:*:0/30
Persistent=true

[Install]
WantedBy=timers.target

I start the timer using systemctl start backup_test.timer. The timer does run every 30 seconds, but the "exclude" part seems to be ignored, and the entire directory is copied.

debugging attempts:

I tried the rsync command with multiple --exclude options instead of the brace expansion, in other words I used --exclude=/dir_a --exclude=/dir_b instead of --exclude={/dir_a,/dir_b}. This actually made the command work. So I think the problem is that the brace expansion is not being done properly.

Next, I tried to test a simpler command with brace expansion, to see if the brace expansion itself was the problem. So I replaced the rsync command in the systemd service with touch /home/trevor/test{1,2}.txt. This command created a file literally named /home/trevor/test{1,2}.txt. So I'm pretty sure the problem is that the brace expansion is not handled correctly by the systemd service.

1 Answer 1

5

I think I figured this out on my own by reading the manual for systemd services (man systemd.service). The ExecStart= option doesn't directly support shell command lines, which I think is why the brace expansion was not being performed as intended. I got everything to work by passing my rsync command to sh -c in the ExecStart= line:

ExecStart=sh -c 'rsync -a --exclude={/dir_a,/dir_b} /home/trevor/test_dir/ /my_ssd/test_dir/'

reference:

$ man systemd.service
...
       Note that shell command lines are not directly supported. If shell
       command lines are to be used, they need to be passed explicitly to a
       shell implementation of some kind. Example:

           ExecStart=sh -c 'dmesg | tac'
...
2
  • Another alternative is to just expand the list of excluded directories yourself: --exclude=/dir_a --exclude=/dir_b. You can also use a file with an exclude list for rsync, which should also work.
    – filbranden
    Feb 13, 2020 at 4:57
  • 1
    Thanks, I didn't know about the file option. Just to make note, that is the --exclude-from=FILE option.
    – Trevor
    Feb 20, 2020 at 0:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .