👈🏻 Some tips for those transitioning from Cron jobs to SystemD timers for scheduled tasks.
If you’re a greybeard who is moving away from cron jobs into the world of SystemD Timers, you might find this helpful. I burned a lot of time learning systemd timers for the first time this week and I want to highlight something for others who have never used SystemD to scheduled tasks.
This tutorial is great: https://www.fosslinux.com/48317/scheduling-tasks-systemd-timers-linux.htm. Follow it to the letter and you should be fine. You might be like me and miss a letter or two. I’d like to save you from some painful debugging & searching.
The general process for scheduling a task with systemd consists of the following steps:
- Create a service file that declares the shell script/binary that should be executed. This service file will express the system state target for when your task should be allowed to execute. A common target is “multi-user.target”. I’ve had some services that did not launch at multi-user runlevels. If you want to be very conservative, you can specify a target of “graphical.target”. You can see options for your targets using the command
systemd-analyze critical-chain
. Additionally, the service file expresses a “type” of service: either simple, exec, forking, oneshot, dbus, notify or idlesimple
,exec
,forking
,oneshot
,dbus
,notify
oridle
. The useful information I’ve had to collect to deciding the type of service are 1) Determine if the script/binary you call forks and 2) Identify if your binary/script/ provides a nonzero return code during execution. You can find the return code of a script using the linux commandecho $?
. Check out https://www.freedesktop.org/software/systemd/man/systemd.service.html for more details on the types you can declare. - Create a timer file that declares the schedule on which the script/binary should be executed. This timer file will express the calendar on which the service is launched. A counterintuitive aspect of scheduled tasks in system d is that it is the timer file that declares the service it manages.
- Enable your service. The systemctl enable command is used to enable a service. Enabling a service means that it will be available after reboot. If you want to launch the service immediately, you can use the command
systemctl --user enable [servicename.service] --now
- Enable your timer. The systemctl enable command is used to enable a service. Enabling a timer means that it will be available after reboot. If you want to launch the timer immediately, you can use the command
systemctl --user enable [timername.timer] --now
You will in all likelihood write a valid .service file for launching commands & .timer file for scheduling the tasks. Systemctl –user enable TheTasks.service will appear to return fine and systemctl –user start TheTasks.service will execute your task- but you may find that the service doesn’t run with the schedule. When you check the status of TheTasks.service, you’ll see something like:
systemctl status TheTasks.service
● TheTasks.service - Service that does Tasks
Loaded: loaded (/home/me/.config/systemd/user/TheTasks.Service; enabled; vendor preset: enabled)
Active: inactive (dead) Since [$dateOfLastReboot]
Process: 883 ExecStart=/usr/bin/script.sh (code=exited, status=0/SUCCESS)
Inactive (dead) means it won’t launch. You’ll second guess yourself and wonder if you’re writing the timer correctly. You probably are- but if you have doubts, definitely checkout the systemd-analyze calendar command to confirm that you’re setting it correctly.
systemd-analyze calendar 'Sun,Thu *-*-* 15:00:00 America/Los_Angeles’
Original form: Sun,Thu *-*-* 15:00:00 America/Los_Angeles
Normalized form: Thu,Sun *-*-* 15:00:00 America/Los_Angeles
Next elapse: Sun 2022-02-27 15:00:00 PST
(in UTC): Sun 2022-02-27 23:00:00 UTC
From now: 18h left
Good news: you are scheduling your tasks correctly. Bad news, searching this error on google is not fruitful. inactive (dead) can happen for a lot of reasons. But one reason which isn’t well covered:
YOU PROBABLY FORGOT TO ENABLE THE .TIMER:
systemctl --user status TheTasks.timer
● TheTasks.timer - Weekly timer that launches tasks
Loaded: loaded (/home/me/.config/systemd/user/TheTasks.timer; disabled; vendor preset: enabled)
Active: inactive (dead)
Trigger: n/a
Triggers: ● TheTasks.service
So you can go ahead and enable that:systemctl --user enable --now TheTasks.timer
And if you check the status a second time:
systemctl --user status TheTasks.timer
● TheTasks.timer - Weekly timer that launches tasks
Loaded: loaded (/home/pi/.config/systemd/user/TheTasks.timer; enabled; vendor preset: enabled)
Active: active (waiting) since Sat 2022-02-26 20:20:57 PST; 5s ago
Trigger: Sun 2022-02-27 15:00:00 PST; 18h left
Triggers: ● TheTasks.service
You’re in great shape. Hope that helps.