Digital journal of Ulises Avila


Using Systemd Timers to schedule your tasks

On linux-administration. 26 May, 2018
Systemd Timers are a powerful tool for scheduling tasks that can replace Crontab without problems.

Introduction

As a sysadmin you will face countless times in which you need to schedule tasks to automate process. The de facto solution is to use cron and crontab, in this tutorial you will meet the built in solution in the systemd service manager called timer.

Requirements

  • Any Linux machine with systemd as service manager, ie Ubuntu, Centos, Fedora, Debian, etc.

Differences with cron

In the following the differences between systemd.timers and cron are presented. Note that they are not arranged in advantages or disadvantages because each sysadmin should choose tools according to experience and preferences.

  • Each task has its own service on systemd.
  • Each task needs two files, one service and the actual timer.
  • Full access ot the systemd ecosystem.
  • Tasks are logged in the systemd journal.
  • There is no mailto equivalent function for sending emails at job failure.

Overview of the process

In order to create a task with systemd.timers you have to do 3 simple steps in that specific order. If you are used to manage custom systemd services this information will not be that new for you, otherwise please review this process.

  1. Creating a service file: They are services files, they end with .service. These ones describe how the service should be treated.
  2. Creating the timer file: They end with .timer. They are also unit files, but you configure how and when the timer activates.
  3. Activating the timer: After having done your files you can start your timer with the help of systemctl.

Note: You should always have a name matching pair of files, one with the .service ending and another one with the .timer ending. Any timer without proper names will fail because the timer file is the one that activates and then controls the service file. The name of your service is pointed in the timer file, so it is better to share the name to ease maintenance operations.

Types of timers

When scheduling tasks we need to ensure that our tool can respond to two situations, tasks at certain date and tasks at intervals of time. Fortunately timers can accomplish this using an easy to use syntax.

  • Realtime timer: These are the ones that activate on a calendar date. You use the OnCalendar option to trigger this type of timers.
  • Monotonic timer: They trigger after a span of time, they can start with the system and other events, but it is recommended to delay their activation to prevent overloading the system. You normally use a combination of OnBootSec and OnActiveSec.

Creating tasks

After having reviewed the overall information of timers, we can start to create examples, one per type in the following part.

Note: Both .service and .timer files must be stored in /etc/systemd/system/.

Create required bash file

Two bash files are created in our home directory, these are the files that we are going to schedule in each of our tasks.

Preparing First file

echo "ping -c 3 google.com >> /home/YOUR_USER/ping_task1.txt" >> /home/YOUR_USER/task1.sh
chmod +x /home/YOUR_USER/task1.sh

Preparing Second file

echo "ping -c 3 google.com >> /home/YOUR_USER/ping_task2.txt" >> /home/YOUR_USER/task2.sh
chmod +x /home/YOUR_USER/task2.sh

Note: systemd.timer is not a substitute of Bash, you have to group your instructions in scripts for better maintenance.

Example 1 Realtime or wallclock timer

The first example should accomplish this: Scheduling a ping to be executed after 20 minutes of the user's real time, then dump its results to a file.

In the task1.service file the service is configured, it is a simple file with its own description and the tasks to execute under the Service part.

[Unit]
Description=Vultr wallclock timer example

[Service]
ExecStart=/bin/bash /home/YOUR_USER/task1.sh

In the task1.timer file the timer is configured, as every unit file we should start with the Unit section, after that the rest of the configuration is written.

For the Timer section we are setting OnCalendar, which is the exact date of activating, it follows an special syntax that you can review some paragraphs below this. The RandomizedDelaySec is just a security matter to randomize activation time, this in order to prevent many services activating in the same time with the risk of running out of memory. Unit under the Timer section corresponds to the name of the service file that was created in the last step, in other words it is task1.service.

The Install section can be copied over here, because it is telling that we are putting this under the control of service.timer.

[Unit]
Description=Run wallclock example 20 minutes after been activated

[Timer]
OnCalendar=YOUR_TIME
RandomizedDelaySec=1min
Unit=task1.service

[Install]
WantedBy=timers.target

In this file YOUR_TIME should be a valid structure with the form: DayOfWeek Year-Month-Day Hour:Minute:Second for example OnCalendar=Wed,Sun *-*-9..14 12:00:00 which translates to Execute every Wednesday and Sunday of all months in all years if they are between day 9 and 14 at 12 hours, time is written in a 24h format. With this you can build your date to activate your task 20 minutes later than your actual date. If you are unsure about your system's time type the date command in your terminal, you will get the actual system's time with this.

Note: in the OnCalendar option there is special syntax, items separated by , are treated as lists, ranges are made using .. and they are inclusive, the wildcard * can represent any accepted value of the position where it is placed.

With this the first task can be activated, take a look that we are manipulating the .timer file, not the .service one:

systemctl enable task2.timer
systemctl start task2.timer

Example 2 Monotonic timer

The second example should accomplish this: Scheduling a ping every 5 minutes from boot up time, delay its first activation time by 5 minutes, then every activation should be after 5 minutes.

The task2.service structure is identical to the first example. Do not forget to write a meaningful description to you, also do not forget to point the correct script int the Service section.

[Unit]
Description=Vultr monotonic timer example

[Service]
ExecStart=/bin/bash /home/YOUR_USER/task2.sh

The task2.timer is slightly different, it uses two operations. OnBootSec tell us that the tasks are activated after an specific time of booting up our system in this case it is 5 minutes after booting. OnUnitActiveSec describes the span of time between activations, in this case it is 5 minutes between consecutive activations. The rest of the file is already explained in the first example.

[Unit]
Description=Run monotonic timer every 5 minutes

[Timer]
OnBootSec=5min
OnUnitActiveSec=5min
Unit=task2.service

[Install]
WantedBy=timers.target

There are many ways to create monotonic timers, their difference is the starting point. OnActiveSec is relative to the moment the timer is activated. OnBootSec is relative to the time the system booted up. OnStartupSec is relative to the time systemd was first started. OnUnitActiveSec is relative to the last time the timer to be activated was last activated. OnUnitInactiveSec is relative to the las time the timer to be activated was last deactivated. You can abuse wildcards and lists in the OnCalendar option to emulate this behavior, but it is not recommended because the date definition can become hard to read and maintain.

Then then the second task is activated:

systemctl enable task2.service
systemctl start task2.service

At this point reboot your computer so you second task is ready to function.

After a succesful reboot, wait for your timers to activate, then go to you home folder and list all your files. If everything went fine you should see your bash files along the files that contain the result of the ping done in the timer:

YOUR_USER@YOUR_HOST:~# ls
ping_task1.txt  ping_task2.txt  task1.sh  task2.sh

The last thing to corroborate that our tasks run successfully is reading their content with the command cat, in this case we are seeing the content of the first task, you can repeat it to list the content of the second task, just beware that its content may be larger due to constant activation and constant append. If you see the content of the ping then everything is perfect.

YOUR_USER@YOUR_HOST:~# cat ping_task1.txt
PING google.com (216.58.216.142) 56(84) bytes of data.
64 bytes from sea15s01-in-f142.1e100.net (216.58.216.142): icmp_seq=1 ttl=57 time=0.246 ms
64 bytes from sea15s01-in-f142.1e100.net (216.58.216.142): icmp_seq=2 ttl=57 time=0.417 ms
64 bytes from sea15s01-in-f142.1e100.net (216.58.216.142): icmp_seq=3 ttl=57 time=0.331 ms

--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.246/0.331/0.417/0.071 ms

Reaching this point we can create basic timers with any time configuration needed, but still we need to learn how manage them.

Management

As stated above, timers are heavily tied to the systemd ecosystem, so we can take advantage of systemd tools.

Listing all timers

The first step to ensure that our timers are working is using the command systemctl list-timers, we should see them listed under the Unit column.

YOUR_USER@YOUR_HOST:~# systemctl list-timers
NEXT                         LEFT          LAST                         PASSED       UNIT                         ACTIVATES
n/a                          n/a           Wed 2018-04-11 18:00:42 UTC  1h 59min ago task1.timer                  task1.service
Wed 2018-04-11 20:02:07 UTC  2min 23s left Wed 2018-04-11 19:57:07 UTC  2min 36s ago task2.timer                  task2.service
Thu 2018-04-12 05:28:39 UTC  9h left       Wed 2018-04-11 07:50:31 UTC  12h ago      apt-daily.timer              apt-daily.service
Thu 2018-04-12 06:28:47 UTC  10h left      Wed 2018-04-11 06:17:40 UTC  13h ago      apt-daily-upgrade.timer      apt-daily-upgrade.service
Thu 2018-04-12 18:13:38 UTC  22h left      Wed 2018-04-11 18:13:38 UTC  1h 46min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2018-04-16 03:56:05 UTC  4 days left   Wed 2018-04-11 18:31:49 UTC  1h 27min ago snapd.refresh.timer          snapd.refresh.service

** Note: To list all timers including the inactive ones, append the --all option to the command.**

If your task1 has already triggered you will see n/a in the columns Next and Left, the first expresses the date of the next trigger and the second the remaining time to be triggered.

Checking the status of your timers

The second thing to ensure wellbeing is checking the status of each timer. To access it just use systemctl status YOUR_TASK.timer, in this case the second timer is checked, everything seems ok because it is active and waiting. If there was an error you could see it in this part.

YOUR_USER@YOUR_HOST:~# systemctl status task2.timer
● task2.timer - Run monotonic timer every 5 minutes
   Loaded: loaded (/etc/systemd/system/task2.timer; enabled; vendor preset: enabled)
   Active: active (waiting) since Wed 2018-04-11 17:58:37 UTC; 2h 7min ago

Apr 11 17:58:37 vultr.guest systemd[1]: Started Run foo weekly and on boot.

Something to note is that when the first task is triggered its status will go from waiting to elapsed but will remain active because its job is to trigger only one time.

Inspecting logs

In this part we are checking again the second timer. One thing to note is that both logs files can be checked, the timer and the service. The command is journalctl -f -u YOUR_UNIT.

As we can see in the logs, the timer file will only be called once, in its activation time.

YOUR_USER@YOUR_HOST:~# journalctl -f -u task2.timer
-- Logs begin at Wed 2018-04-11 17:58:36 UTC. --
Apr 11 17:58:37 vultr.guest systemd[1]: Run monotonic timer every 5 minutes.

Remember that the service file is the one to be controlled, that is why we see and entry for each activation.

YOUR_USER@YOUR_HOST:~# journalctl -f -u task2.service
-- Logs begin at Wed 2018-04-11 17:58:36 UTC. --
Apr 11 19:25:52 vultr.guest systemd[1]: Started Vultr monotonic timer example.
Apr 11 19:31:04 vultr.guest systemd[1]: Started Vultr monotonic timer example.
Apr 11 19:36:08 vultr.guest systemd[1]: Started Vultr monotonic timer example.
Apr 11 19:41:36 vultr.guest systemd[1]: Started Vultr monotonic timer example.

The absence of content of both logs means that something not desired is happening, before jumping to conclusions check that your timers are activated.

Enabling and starting

To manage enabling and starting our timers systemd syntax is used: enable to activate on boot, disable to remove this activation, start to initiate the timer right now without boot activation, stop to cancel a timer in execution, if its enabled it will activate at the next boot or when we start it again with start.

Conclusion and further reading

You dealt with a lot of information this time. The benefits of this are increasing your systemd knowledge and getting to know a simpler way of scheduling tasks in the Linux ecosystem. At first timers appear to be more complicated than should be, after all with cron you only have to append your task to the crontab file and let it execute itself. But basic things like logging can become cumbersome if you are jumping distributions. Timers resolve that and other problems by letting systemd doing the heavy lifting. The next step in your journey would we studying the unit file definitions to expand your knowledge in things like controlling starting and ending events or including other systemd elements as requirements.