👈🏻 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:

  1. 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 idle simpleexecforkingoneshotdbusnotify or idle. 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 command echo $?. Check out https://www.freedesktop.org/software/systemd/man/systemd.service.html for more details on the types you can declare.
  2. 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.
  3. 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
  4. 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.