From crontabs to Systemd timers

First written onJuly 4, 2019
Last updated onOctober 8, 2022

Introduction #

Since I started my own server at home I have always use crontabs to handle recurring tasks such as backups.

Please note that every step described here is directly related to the automated-tasks repository.

The problem of this method is that you cannot easily control running processes and you are not provided with a uniform interface. Systemd service and timer unit files seem to solve these issues. As usual, I followed the Arch wiki.

Preparation #

To avoid complications, I prefer having all relevant scripts of all the users in one place.

Users and groups #

I created a new user and group called jobs

# useradd -m -s /bin/bash -U jobs

Every user ${user} that needs to perform some task must be added to the jobs group.

# usermod -aG jobs ${user}

New directories #

Scripts and services are separated into two different directories. We will make these directories accessible to the jobs group only.

# mkdir -p /home/jobs/{scripts,services}/by-user
# chmod -R 070 /home/jobs
# chown -R jobs:jobs /home/jobs

Example #

As an example I will show you the unit files I wrote for one of my daily backups.

Script #

Copy the backup script and the configuration file in /home/jobs/scripts/by-user/root, and change ownerships and permissions

# chmod -R 700 /home/jobs/scripts/by-user/root
# chown -R root:root /home/jobs/scripts/by-user/root

Use the appropriate user and group for the ownership.

Service unit file #

The service file is called backup-data.service and needs to be placed in /home/jobs/services/by-user/root

Description=data backup

ExecStart=-/home/jobs/scripts/by-user/root/backup.sh '/data/*' /mnt/backup_data


Timer unit file #

Place the following timer file, called backup-data.timer, under /home/jobs/services/by-user/root

Description=Once a day backup data

OnCalendar=*-*-* 4:00:00


Set the permissions and ownership #

Since the backup-data files must be run by root we must fix the permissions and ownerships

# chmod -R 700 /home/jobs/services/by-user/root
# chown -R root:root /home/jobs/services/by-user/root

Fix the ownerships with the appropriate user depending on the directory. These steps are useful just to prevent mistakes. All these files will be loaded by systemd and accessible from other users anyway, for example via /etc/systemd/system/backup-data.service.

Deployment #

To simplify the deployment of the services and timers I wrote this script, called deploy.sh, which needs to be placed in /home/jobs/services.

#!/usr/bin/env bash

set -euo pipefail


[ ${UID} -eq 0 ]

find "${SRC_DIR}" \( -name "*.service" -o -name "*.timer" \) \
    -type f -exec cp {} "${DST_DIR}" \;
systemctl daemon-reload
timers=$(find "${SRC_DIR}" -name "*.timer" -type f -printf "%f\n")
systemctl start ${timers}
systemctl enable ${timers}

This script will copy the service and timer files in the appropriate directory and enable the new timers. Before running it, change its permissions and ownership

# chown root:root /home/jobs/services/deploy.sh
# chmod 700 /home/jobs/services/deploy.sh

Now, run the previous script and check the status of the systemd timers

# cd /home/jobs/services
# ./deploy.sh
$ systemctl list-timers --all

You should see something like

NEXT                          LEFT      LAST  PASSED  UNIT                ACTIVATE
Fri 2019-07-05 04:00:00 CEST  9h left   n/a   n/a     backup-data.timer   backup-data.service

1 timer listed.

File and directory structure #

Here is a representation of the files and directories mentioned in this post

|-- scripts
|   |-- by-user
|       `-- root
|           |-- backup.conf
|           |-- backup.sh
`-- services
    |-- by-user
    |   `-- root
    |       |-- backup-data.service
    |       |-- backup-data.timer
    `-- deploy.sh