|
|
|
---
|
|
|
|
title: From crontabs to Systemd timers
|
|
|
|
tags: [bash, shell, crontab, cronie, systemd, timer]
|
|
|
|
updated: 2021-03-01 17:57
|
|
|
|
description: The steps I followed to migrate from crontabs to systemd timers.
|
|
|
|
---
|
|
|
|
|
|
|
|
## Introduction
|
|
|
|
|
|
|
|
<!--excerpt_start-->
|
|
|
|
Since I started my own server at home I have always use crontabs to handle
|
|
|
|
recurring tasks such as backups.
|
|
|
|
<!--excerpt-end-->
|
|
|
|
|
|
|
|
*Please note that every step described here is directly related to the
|
|
|
|
[automated-tasks](https://github.com/frnmst/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](https://wiki.archlinux.org/index.php/Systemd/Timers)
|
|
|
|
[wiki](https://wiki.archlinux.org/index.php/Systemd#Writing_unit_files).
|
|
|
|
|
|
|
|
## 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]({{ site.baseurl }}/notes/my-backup-system.html).
|
|
|
|
|
|
|
|
### Script
|
|
|
|
|
|
|
|
Copy the [backup script]({{ site.baseurl }}/notes/my-backup-system.html#incremental-backup-script)
|
|
|
|
and the [configuration file]({{ site.baseurl }}/notes/my-backup-system.html#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`
|
|
|
|
|
|
|
|
```shell
|
|
|
|
[Unit]
|
|
|
|
Description=data backup
|
|
|
|
Requires=mnt-backup_data.mount
|
|
|
|
Requires=data.mount
|
|
|
|
After=mnt-backup_data.mount
|
|
|
|
After=data.mount
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
Type=simple
|
|
|
|
ExecStart=-/home/jobs/scripts/by-user/root/backup.sh '/data/*' /mnt/backup_data
|
|
|
|
User=root
|
|
|
|
Group=root
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
WantedBy=multi-user.target
|
|
|
|
```
|
|
|
|
|
|
|
|
### Timer unit file
|
|
|
|
|
|
|
|
Place the following timer file, called `backup-data.timer`,
|
|
|
|
under `/home/jobs/services/by-user/root`
|
|
|
|
|
|
|
|
```shell
|
|
|
|
[Unit]
|
|
|
|
Description=Once a day backup data
|
|
|
|
|
|
|
|
[Timer]
|
|
|
|
OnCalendar=*-*-* 4:00:00
|
|
|
|
Persistent=true
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
WantedBy=timers.target
|
|
|
|
```
|
|
|
|
|
|
|
|
### 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`.
|
|
|
|
|
|
|
|
```shell
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
SRC_DIR='by-user/'
|
|
|
|
DST_DIR='/etc/systemd/system'
|
|
|
|
|
|
|
|
[ ${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
|
|
|
|
|
|
|
|
```
|
|
|
|
/home/jobs
|
|
|
|
|-- scripts
|
|
|
|
| |-- by-user
|
|
|
|
| `-- root
|
|
|
|
| |-- backup.conf
|
|
|
|
| |-- backup.sh
|
|
|
|
`-- services
|
|
|
|
|-- by-user
|
|
|
|
| `-- root
|
|
|
|
| |-- backup-data.service
|
|
|
|
| |-- backup-data.timer
|
|
|
|
`-- deploy.sh
|
|
|
|
```
|
|
|
|
|
|
|
|
~
|