When it comes to scheduling jobs in Linux, cron was always king. I don't think it had been dethroned yet, but systemd timers have their benefits. In this tutorial we will discuss systemd timers and how they compare to cron jobs. We will also walk you through creating your first systemd timer as well as give some pointers to ensure a smooth transition from cron.
Table of Contents
SystemD Timer Units vs Cron
While systemd timer units can be used to schedule jobs similar to cron, there are distinct pros and cons to each.
Systemd timer units are basically systemd services and with that comes all the capabilities and benefits, including but not limited to:
- Jobs can have dependencies (Can depend on other systemd services).
- Timer units are logged to the systemd journal.
- You can easily run a job independent of it's timer.
- Timer units can have a nice value or use cgroups for resource management.
- Systemd timer units can be triggered by events like boot and hardware changes.
- Can be easily disabled or enabled using systemctl.
- Timer units can use real time or monotonic time.
Cron has two glaring benefits over systemd timers.
- Configuring cron jobs is a simple process.
- Cron is capable of sending emails using the MAILTO function.
Creating a Timer Unit
In this section we will create a timer unit to backup the home directory to a USB drive when the system boots and then every 24 hours while the system is up. In order to create a timer unit we need three items.
- Script for the service unit to run.
- Service unit file.
- Timer unit file.
Create a Script to Run the rsync Command.
We will create the following file called my-rsync.sh in /usr/local/bin/.
#!/bin/bash
rsync -aP /home/ /mnt/usb/
Let's make the file executable:
chmod +x /usr/local/bin/my-rsync.sh
Create Service Unit File to Execute the Script
The service unit will be responsible for running the script in systemd. In this example we will create the service unit with a basic configuration for simplicity.
We will create a file called my-rsync.service in /etc/systemd/system/ with the following lines:
[Unit]
Description="A script to backup all home dirs to usb drive"
[Service]
ExecStart=/usr/local/bin/my-rsync.sh
Creating the Timer Unit File
The last file needed is the timer file to define when the service should run. A timer file is placed in the same directory as it's corresponding service file and should have the same name, but with a .timer extension.
We will create a file called my-rsync.timer in /etc/systemd/system/ with the following lines:
[Unit]
Description="Backs up the home directory"
[Timer]
OnBootSec=3min
OnUnitActiveSec=24h
Unit=my-rsync.service
[Install]
WantedBy=multi-user.target
OnBootSec is the time to wait after boot. OnUnitActiveSec defines the time relative to the last time the unit was activated. WantedBy basically sets a dependency resulting in our timer starting when the system has reached the multi-user target (runlevel 3).
Verify the Correctness of Your Unit Files
You can use the systemd-analyze command with the verify option to check the correctness of your unit files. It will help point out any syntax errors.
$ sudo systemd-analyze verify /etc/systemd/system/my-rsync.*
[savona@centos7 bin]$
If nothing is returned, then your file has passed verification.
Start Your systemd Timer
You are now ready to start your timer. You can start the timer using systemctl.
$ sudo systemctl start my-rsync.timer
Enabling Your systemd Timer on Boot
You can enable your new systemd timer just as you would any service.
$ sudo systemctl enable my-rsync.timer
Manually Running Your Job
If you want to manually run the job you can always start the service. Here is an example:
$ ls -l /mnt/usb/
total 0
$ sudo systemctl start my-rsync.service
$ ls -l /mnt/usb/
total 40
drwxr-xr-x. 5 savona savona 4096 Mar 3 18:40 Desktop
drwxr-xr-x. 2 savona savona 4096 Jan 23 23:10 Documents
drwxr-xr-x. 2 savona savona 4096 Jan 23 23:10 Downloads
-rwxrwxr-x. 1 savona savona 104 Feb 28 21:05 hello.sh
drwxr-xr-x. 2 savona savona 4096 Jan 23 23:10 Music
drwxr-xr-x. 2 savona savona 4096 Jan 23 23:10 Pictures
drwxr-xr-x. 2 savona savona 4096 Jan 23 23:10 Public
drwxrwxr-x. 2 savona savona 4096 Feb 28 21:18 scripts
drwxr-xr-x. 2 savona savona 4096 Jan 23 23:10 Templates
drwxr-xr-x. 2 savona savona 4096 Jan 23 23:10 Videos
In this case we put the script in our PATH, so you can also manually run it by calling the script by name.
$ my-rsync.sh
Checking your Systemd Timer Logs with Journalctl
As mentioned above, one of the benefits of using systemd timers is that they are logged to the systemd journal. We can now check the logs for our service using journalctl with the -u (unit) option.
$ sudo journalctl -u my-rsync
-- Logs begin at Sun 2019-04-07 19:00:33 EDT, end at Sun 2019-04-07 19:05:55 EDT. --
Apr 07 19:03:39 centos7 systemd[1]: Started "A script to backup all home dirs to usb drive".
Apr 07 19:03:39 centos7 my-rsync.sh[14589]: sending incremental file list
Apr 07 19:03:39 centos7 my-rsync.sh[14589]: ./
Apr 07 19:03:39 centos7 my-rsync.sh[14589]: .ICEauthority
Apr 07 19:03:39 centos7 my-rsync.sh[14589]: [105B blob data]
Apr 07 19:03:39 centos7 my-rsync.sh[14589]: .bash_history
Apr 07 19:03:39 centos7 my-rsync.sh[14589]: [105B blob data]
...OUTPUT TRUNCATED...
Conclusion
In this tutorial we discussed the benefits of systemd timer units over cron jobs. This is not to belittle the importance of cron. Both tools have their benefits, and sometimes less is more. If you simply need to run a job every weeknight at 1800, cron would be my choice. If you need to start a job 30 seconds after boot, only if the network is available, then a systemd unit is probably preferable.