Using systemd timers instead of cron jobs

I am currently in the process of replacing my cron jobs with systemd timers. I have been using timers for several years, but usually I did not go deep into the intricacies of their use, figuring out only what was needed to complete the task of interest to me. I recently worked on a series of articles on systemd and found out that systemd timers have some very interesting features.







These timers, like cron jobs, can, at a given time, trigger various actions on the system. For example - running shell scripts or programs. Timers can work, for example, once a day, and only on Mondays. Another example is a timer that fires every 15 minutes during business hours (from 8 am to 6 pm). But systemd timers can do something that cron jobs can't. For example, a timer can call a script or program a specified time after an event. Such an event can be the system boot or systemd startup, the completion of a previous task, or even the termination of a service that was previously called by a timer.



Timers used for system maintenance



When Fedora or another systemd-based Linux distribution is installed on a computer, several timers are created as part of the system's maintenance routines. These procedures are automatically performed on any Linux system. The corresponding timers trigger various service tasks, such as updating system databases, clearing temporary directories, rotating log files, and so on.



As an example, I will give here information about the timers that are available on the virtual machine that I used for experiments. Here, to get a list of all timers, I used the commandsystemctl status *timer... The asterisk wildcard plays the same role here as it does in other similar commands. Namely, it tells the system that we are interested in all the timers (timer units, they are also called "timer unit files" or "timer units") of systemd:



[root@testvm1 ~]# systemctl status *timer
● mlocate-updatedb.timer - Updates mlocate database every day
     Loaded: loaded (/usr/lib/systemd/system/mlocate-updatedb.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:00:00 EDT; 15h left
   Triggers: ● mlocate-updatedb.service

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Updates mlocate database every day.

● logrotate.timer - Daily rotation of log files
     Loaded: loaded (/usr/lib/systemd/system/logrotate.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:00:00 EDT; 15h left
   Triggers: ● logrotate.service
       Docs: man:logrotate(8)
             man:logrotate.conf(5)

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Daily rotation of log files.

● sysstat-summary.timer - Generate summary of yesterday's process accounting
     Loaded: loaded (/usr/lib/systemd/system/sysstat-summary.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 00:07:00 EDT; 15h left
   Triggers: ● sysstat-summary.service

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Generate summary of yesterday's process accounting.

● fstrim.timer - Discard unused blocks once a week
     Loaded: loaded (/usr/lib/systemd/system/fstrim.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Mon 2020-06-08 00:00:00 EDT; 3 days left
   Triggers: ● fstrim.service
       Docs: man:fstrim

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Discard unused blocks once a week.

● sysstat-collect.timer - Run system activity accounting tool every 10 minutes
     Loaded: loaded (/usr/lib/systemd/system/sysstat-collect.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Thu 2020-06-04 08:50:00 EDT; 41s left
   Triggers: ● sysstat-collect.service

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Run system activity accounting tool every 10 minutes.

● dnf-makecache.timer - dnf makecache --timer
     Loaded: loaded (/usr/lib/systemd/system/dnf-makecache.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Thu 2020-06-04 08:51:00 EDT; 1min 41s left
   Triggers: ● dnf-makecache.service

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started dnf makecache –timer.

● systemd-tmpfiles-clean.timer - Daily Cleanup of Temporary Directories
     Loaded: loaded (/usr/lib/systemd/system/systemd-tmpfiles-clean.timer; static; vendor preset: disabled)
     Active: active (waiting) since Tue 2020-06-02 08:02:33 EDT; 2 days ago
    Trigger: Fri 2020-06-05 08:19:00 EDT; 23h left
   Triggers: ● systemd-tmpfiles-clean.service
       Docs: man:tmpfiles.d(5)
             man:systemd-tmpfiles(8)

Jun 02 08:02:33 testvm1.both.org systemd[1]: Started Daily Cleanup of Temporary Directories.


Each timer is associated with at least six lines containing information about it:



  • The first line contains the name of the timer file and a short description of the purpose of the timer's existence.
  • The second line displays information about the state of the timer. Namely, it reports whether it is loaded, gives the full path to the timer file, shows the vendor preset state (disabled or enabled).
  • The third line shows information about the activity of the timer, which includes information about when the timer was activated.
  • The fourth line contains the date and time of the next start of the timer and the approximate time remaining until it starts.
  • The fifth line gives the name of the service or event called by the timer.
  • Some (but not all) systemd timer unit files contain pointers to documentation. Such pointers are, in my example, in the descriptions of three timers.
  • The last line in the timer description is the log entry that is associated with the most recent instance of the service called by the timer.


If you try to execute a command on your computer systemctl status *timer, the set of timers presented to it may well differ from mine.



Timer creation



Although we could figure out the specifics of how timers work by analyzing any existing timers, I suggest creating your own service unit file (service configuration file, service unit ) and a timer file with which the corresponding service will be called. We are here, in order not to complicate the story, give a rather trivial example. But after we deal with it, it will be easier for you to understand the work of other timers.



First, let's create a simple service configuration file that will run something as simple as a command free. For example, this may be needed if we need to regularly monitor the amount of free memory. Let's create a unit file named myMonitor.servicein the folder /etc/systemd/system. It doesn't have to be executable.



# This service unit is for testing timer units
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=Logs system statistics to the systemd journal
Wants=myMonitor.timer

[Service]
Type=oneshot
ExecStart=/usr/bin/free

[Install]
WantedBy=multi-user.target


This file is probably the simplest service configuration file. Now let's check its status and test it to make sure that it works as expected.



[root@testvm1 system]# systemctl status myMonitor.service
● myMonitor.service - Logs system statistics to the systemd journal
     Loaded: loaded (/etc/systemd/system/myMonitor.service; disabled; vendor preset: disabled)
     Active: inactive (dead)
[root@testvm1 system]# systemctl start myMonitor.service
[root@testvm1 system]#


Why isn't anything being output to the console? This is because, by default, standard output ( stdout) from programs started by systemd using service unit files is redirected to the systemd log. Due to this, at least as long as the corresponding records exist, these records can be analyzed. Let's take a look at the log and look for entries related to our service and the day we tested. The corresponding command will look like this : journalctl -S today -u myMonitor.service. The key -Sis an abbreviated version --since. It allows you to specify the time period for which the utilityjournalctllooking for records. The point is not that we are not interested in earlier results. In our case, such results simply will not be. This key is used to reduce the time the utility needs to search for data. If the computer has been working for a long time, a lot of entries can accumulate in the log.



[root@testvm1 system]# journalctl -S today -u myMonitor.service
-- Logs begin at Mon 2020-06-08 07:47:20 EDT, end at Thu 2020-06-11 09:40:47 EDT. --
Jun 11 09:12:09 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 11 09:12:09 testvm1.both.org free[377966]:               total        used        free      shared  buff/cache   available
Jun 11 09:12:09 testvm1.both.org free[377966]: Mem:       12635740      522868    11032860        8016     1080012    11821508
Jun 11 09:12:09 testvm1.both.org free[377966]: Swap:       8388604           0     8388604
Jun 11 09:12:09 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
[root@testvm1 system]#


A task that is launched using a service configuration file can be represented as a single program, a sequence of programs, or a script written in any scripting language. Let's add myMonitor.serviceanother task to the unit file , including the [Service]following at the end of the section :



ExecStart=/usr/bin/lsblk


Let's start the service again and check the log. There should be something in there that resembles what is shown below. Namely, the log should contain the data output by both commands:



Jun 11 15:42:18 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 11 15:42:18 testvm1.both.org free[379961]:               total        used        free      shared  buff/cache   available
Jun 11 15:42:18 testvm1.both.org free[379961]: Mem:       12635740      531788    11019540        8024     1084412    11812272
Jun 11 15:42:18 testvm1.both.org free[379961]: Swap:       8388604           0     8388604
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: sda             8:0    0  120G  0 disk
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: β”œβ”€sda1          8:1    0    4G  0 part /boot
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: └─sda2          8:2    0  116G  0 part
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   β”œβ”€VG01-root 253:0    0    5G  0 lvm  /
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   β”œβ”€VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   β”œβ”€VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   β”œβ”€VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   β”œβ”€VG01-var  253:4    0   20G  0 lvm  /var
Jun 11 15:42:18 testvm1.both.org lsblk[379962]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 11 15:42:18 testvm1.both.org lsblk[379962]: sr0            11:0    1 1024M  0 rom
Jun 11 15:42:18 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 11 15:42:18 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.


Now, after we are sure that everything is working correctly, we will create, in the folder /etc/systemd/system, a timer unit file, giving it a name myMonitor.timer. Add the following to the file:



# This timer unit is for testing
# By David Both
# Licensed under GPL V2
#

[Unit]
Description=Logs some system statistics to the systemd journal
Requires=myMonitor.service

[Timer]
Unit=myMonitor.service
OnCalendar=*-*-* *:*:00

[Install]
WantedBy=timers.target


The timestamp OnCalendarin this file,, *-*-* *:*:00should cause the timer to call the unit myMonitor.serviceevery minute. OnCalendarWe'll talk more about it below.



In the meantime, we can take a look at the log entries related to starting the timer service. We can also turn on the timer watch mode. However, monitoring the service will allow you to see the results in near real time. To do this, you need to run journalctlwith the key -f( follow):



[root@testvm1 system]# journalctl -S today -f -u myMonitor.service
-- Logs begin at Mon 2020-06-08 07:47:20 EDT. --


Start the timer, but do not include it in autostart at system boot. 



[root@testvm1 ~]# systemctl start myMonitor.timer
[root@testvm1 ~]#


Watch what happens for a while.



One of the results appears immediately. And the next ones will be displayed at intervals of about one minute. Watch the magazine for a few minutes.



[root@testvm1 system]# journalctl -S today -f -u myMonitor.service
-- Logs begin at Mon 2020-06-08 07:47:20 EDT. --
Jun 13 08:39:18 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:39:18 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:39:19 testvm1.both.org free[630566]:               total        used        free      shared  buff/cache   available
Jun 13 08:39:19 testvm1.both.org free[630566]: Mem:       12635740      556604    10965516        8036     1113620    11785628
Jun 13 08:39:19 testvm1.both.org free[630566]: Swap:       8388604           0     8388604
Jun 13 08:39:18 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: sda             8:0    0  120G  0 disk
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: β”œβ”€sda1          8:1    0    4G  0 part /boot
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: └─sda2          8:2    0  116G  0 part
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   β”œβ”€VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   β”œβ”€VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   β”œβ”€VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   β”œβ”€VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   β”œβ”€VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:39:19 testvm1.both.org lsblk[630567]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:39:19 testvm1.both.org lsblk[630567]: sr0            11:0    1 1024M  0 rom
Jun 13 08:40:46 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:40:46 testvm1.both.org free[630572]:               total        used        free      shared  buff/cache   available
Jun 13 08:40:46 testvm1.both.org free[630572]: Mem:       12635740      555228    10966836        8036     1113676    11786996
Jun 13 08:40:46 testvm1.both.org free[630572]: Swap:       8388604           0     8388604
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: sda             8:0    0  120G  0 disk
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: β”œβ”€sda1          8:1    0    4G  0 part /boot
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: └─sda2          8:2    0  116G  0 part
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   β”œβ”€VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   β”œβ”€VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   β”œβ”€VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   β”œβ”€VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   β”œβ”€VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:40:46 testvm1.both.org lsblk[630574]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:40:46 testvm1.both.org lsblk[630574]: sr0            11:0    1 1024M  0 rom
Jun 13 08:40:46 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:40:46 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.
Jun 13 08:41:46 testvm1.both.org systemd[1]: Starting Logs system statistics to the systemd journal...
Jun 13 08:41:46 testvm1.both.org free[630580]:               total        used        free      shared  buff/cache   available
Jun 13 08:41:46 testvm1.both.org free[630580]: Mem:       12635740      553488    10968564        8036     1113688    11788744
Jun 13 08:41:46 testvm1.both.org free[630580]: Swap:       8388604           0     8388604
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: sda             8:0    0  120G  0 disk
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: β”œβ”€sda1          8:1    0    4G  0 part /boot
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: └─sda2          8:2    0  116G  0 part
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   β”œβ”€VG01-root 253:0    0    5G  0 lvm  /
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   β”œβ”€VG01-swap 253:1    0    8G  0 lvm  [SWAP]
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   β”œβ”€VG01-usr  253:2    0   30G  0 lvm  /usr
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   β”œβ”€VG01-tmp  253:3    0   10G  0 lvm  /tmp
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   β”œβ”€VG01-var  253:4    0   20G  0 lvm  /var
Jun 13 08:41:47 testvm1.both.org lsblk[630581]:   └─VG01-home 253:5    0   10G  0 lvm  /home
Jun 13 08:41:47 testvm1.both.org lsblk[630581]: sr0            11:0    1 1024M  0 rom
Jun 13 08:41:47 testvm1.both.org systemd[1]: myMonitor.service: Succeeded.
Jun 13 08:41:47 testvm1.both.org systemd[1]: Finished Logs system statistics to the systemd journal.


Be sure to check both the timer status and the service status.



Did you manage to notice here what I noticed? You may have noticed at least two things as you read the magazine.



Firstly - the fact that you do not need to do something special for logging that ExecStartof myMonitor.servicewriting in stdout. This feature is part of the standard systemd startup functions. But this means that you will probably need to be careful when running scripts from service configuration files, noting how much data they write to stdout.



Secondly, you may have noticed that the timer does not start exactly at:00seconds of every minute, and not even exactly one minute after the last run. This is one of the features of such timers, if it is necessary (or if it hurts the feelings of the system administrator), this behavior of the timers can be changed by making them more accurate.



The reason that the timer does not start in :00seconds of every minute is because the system thus tends to prevent multiple services from starting simultaneously. For example, when setting up the time indicator, OnCalendaryou can use values ​​such as Weekly, Dailyand others. These shorthand ways of naming points in time are configured so that the timers in which they are used start at00:00:00the corresponding day. When multiple timers are configured this way, the chances are high that they will all fire at the same time.



This is why systemd timers are deliberately designed so that they do not start at exactly the specified time, but with some random deviation from it. This deviation cannot be called completely accidental. Timers start somewhere in a time window that starts at the specified moment and ends at one minute from the original. This time, in accordance with the documentation for systemd.timer, is maintained in a stable state, taking into account all other timers declared in the system. In the above log fragment, you can see that the timer is triggered immediately after it starts, and then - approximately 46 or 47 seconds after the start of each next minute.



Most often, such a probabilistic approach to determining the exact timing of the timers suits everyone. When scheduling tasks such as making a backup copy of something while such tasks are performed outside of business hours, this does not cause any problems. The system administrator, setting up cron jobs, can specify clearly defined times to start them, something like 01:05:00trying to ensure that these jobs do not conflict with others. There is a wide range of ways of specifying the time that allow this. Random changes in the start time of a task that do not exceed a minute usually do not play a special role.



But for some tasks, the exact timing of the timer is extremely important. In such cases, when setting the timers, you can specify a more accurate time of their operation (up to the accuracy, measured in microseconds). This is done by adding to the timer description file, in the section Timer, a construction that resembles the following:



AccuracySec=1us


You can use special keywords to specify the desired precision of the timer. These keywords can also be used when setting up recurring and one-time events. The system understands the following keywords:



  • Microsecond: usec, us, Β΅s.
  • Millisecond: msec, ms.
  • Second: seconds, second, sec, s.
  • Minute: minutes, minute, min, m.
  • Hour: hours, hour, hr, h.
  • Day: days, day, d.
  • Week: weeks, week, w.
  • Month: months, month, M(the month is defined as 30.44 days).
  • Year: years, year, y(the year is defined as 365.25 days).


All standard timers that are available in /usr/lib/systemd/systemare configured using much longer ranges that set the accuracy of their triggering, since in the case of these timers, their triggering at a strictly specified time is not particularly important. Take a look at the specs of some of the system generated timers:



[root@testvm1 system]# grep Accur /usr/lib/systemd/system/*timer
/usr/lib/systemd/system/fstrim.timer:AccuracySec=1h
/usr/lib/systemd/system/logrotate.timer:AccuracySec=1h
/usr/lib/systemd/system/logwatch.timer:AccuracySec=12h
/usr/lib/systemd/system/mlocate-updatedb.timer:AccuracySec=24h
/usr/lib/systemd/system/raid-check.timer:AccuracySec=24h
/usr/lib/systemd/system/unbound-anchor.timer:AccuracySec=24h
[root@testvm1 system]#


To get a better understanding of the internal structure of the timer files from the directory /usr/lib/systemd/system, you can view their contents.



You do not need to configure our learning timer to activate when your system boots. However, if you want, you can use the following command for this:



[root@testvm1 system]# systemctl enable myMonitor.timer


The timer files that you will create do not need to be executable. In addition, service configuration files do not need to be configured to be activated at boot, as they are called by timers. If necessary, the service can also be called manually from the command line. Try this and look in the systemd log.



To learn more about the accuracy of timers, how to specify the time of event triggering, and how to trigger events, see the documentation for systemd.timerand systemd.time.



Timer types



Systemd timers have other features that cron jobs do not have, one-off or repetitive, that are invoked only with real-time and real-time dates. Systemd timers can be configured to be called based on changes in the state of other systemd units. For example, the timer can be configured so that it would be triggered after a specified time after the system boots, after the user logs into it, or after a specified time after activating a certain service. These timers are called monotonic. These timers are reset after every system reboot.



The following table shows a list of monotonic timers with a brief description of each of them. There is also a description of the timer.OnCalendar, which is not monotonous and is used in cases where you need to organize a one-time or repeated launch of something in the future. This table is based on documentation systemd.timer.

Timer Monotone Description
OnActiveSec=


X The timer operation time is set relative to the moment of the timer activation.
OnBootSec=


X The timer is set relative to the moment the system boots.
OnStartupSec=


X . OnBootSec=, . , , , , , .
OnUnitActiveSec=


X , , , .
OnUnitInactiveSec=


X , , , .
OnCalendar=


  . systemd.time(7). OnActiveSec=. β€” systemd, , cron.


When setting monotonic timers, the same keywords can be used as described above when talking about AccuracySec. But it should be noted that systemd converts the corresponding time intervals to seconds. For example, you might want to set a timer that fires once, five days after the system boots. It may look like a description like this: OnBootSec=5d. If the computer was booted 2020-06-15at 09:45:27, the timer will start 2020-06-20at 09:45:27(or within 1 minute after this point in time).



Description of calendar events



Applying calendar events is a key part of describing timers that are called at regular intervals. Let us examine some of the features of such events used when setting the time indicator OnCalendar.



Systemd and its corresponding timers use a different time and date format than crontab. This format is more flexible than the one used in crontab. It allows you to specify the date and time in a simplified way, in command style at. For those familiar with it at, it should be easy to understand the systemd timer settings.



When used OnCalendar=to configure timers, the following basic format is used for specifying the date and time:



DOW YYYY-MM-DD HH:MM:SS


DOW(Day Of Week), this is an optional part of the above construct. In other fields, you can use the asterisk ( *) symbol to represent any value that may appear in the position it occupies. All forms of indication of date and time are converted to normalized form. If no time is specified, it is assumed to be 00:00:00. If the date is not specified, but the time is specified, then the timer will work either on the day it starts (relatively speaking, "today"), or the next day ("tomorrow"). It depends on the current time. Months and days of the week can be named by names. You can use comma-separated lists of values ​​here. Ranges of values ​​can be separated by three dots ( …) between the start and end value of the range.



When specifying dates, we have a couple of interesting options at our disposal. Thus, a tilde (~) can be used to indicate the last day of a month, or to indicate a date a given number of days before the last day of the month. The forward slash (/) can be used as a modifier to indicate the day of the week.



The following table shows some typical examples of timing used in an expression OnCalendar.



Example of presenting a calendar event DOW YYYY-MM-DD HH:MM:SS

Description
*-*-* 00:15:30


Every day of every month of every year, 15 minutes 30 seconds after midnight.
Weekly


Every Monday at 00:00:00.
Mon *-*-* 00:00:00


The same as Weekly.
Mon


The same as Weekly.
Wed 2020-*-*


Every Wednesday 2020 at 00:00:00.
Mon..Fri 2021-*-*


Every weekday in 2021 at 00:00:00.
2022-6,7,8-1,15 01:15:00


June 1 and 15, July and August 2022 01:15:00after midnight.
Mon *-05~03


, , , 3 .
Mon..Fri *-08~04


, 4 , .
*-05~03/2


3 , , β€” , . . , ~.
*-05-03/2


, β€” . . , (-).


,



Systemd has a great tool for checking and examining calendar event specifications. This is a team systemd-analyze calendarthat parses descriptions of calendar events and presents them in a normalized form. This command also provides other interesting information, such as the date and time of the next such event, and the approximate time remaining until that moment.



First, let's take a look at the specification, which contains only the date, does not contain information about the time (note that the times in the fields Next elapseand (in UTC)are different, this difference depends on the local time zone):



[student@studentvm1 ~]$ systemd-analyze calendar 2030-06-17
  Original form: 2030-06-17                
Normalized form: 2030-06-17 00:00:00        
    Next elapse: Mon 2030-06-17 00:00:00 EDT
       (in UTC): Mon 2030-06-17 04:00:00 UTC
       From now: 10 years 0 months left    
[root@testvm1 system]#


Now let's add time information to the description. In this example, date and time are analyzed separately, as entities that are not related to each other:



[root@testvm1 system]# systemd-analyze calendar 2030-06-17 15:21:16
  Original form: 2030-06-17                
Normalized form: 2030-06-17 00:00:00        
    Next elapse: Mon 2030-06-17 00:00:00 EDT
       (in UTC): Mon 2030-06-17 04:00:00 UTC
       From now: 10 years 0 months left    

  Original form: 15:21:16                  
Normalized form: *-*-* 15:21:16            
    Next elapse: Mon 2020-06-15 15:21:16 EDT
       (in UTC): Mon 2020-06-15 19:21:16 UTC
       From now: 3h 55min left              
[root@testvm1 system]#


Now consider an example where date and time are considered together. To do this, they must be enclosed in quotes. But if you use such a construction in OnCalendar, do not forget to remove the quotes, otherwise you will run into errors:



[root@testvm1 system]# systemd-analyze calendar "2030-06-17 15:21:16"
Normalized form: 2030-06-17 15:21:16        
    Next elapse: Mon 2030-06-17 15:21:16 EDT
       (in UTC): Mon 2030-06-17 19:21:16 UTC
       From now: 10 years 0 months left    
[root@testvm1 system]#


Now let's check something from the previous table. I especially like this description from her:



2022-6,7,8-1,15 01:15:00


Let's analyze it:



[root@testvm1 system]# systemd-analyze calendar "2022-6,7,8-1,15 01:15:00"
  Original form: 2022-6,7,8-1,15 01:15:00
Normalized form: 2022-06,07,08-01,15 01:15:00
    Next elapse: Wed 2022-06-01 01:15:00 EDT
       (in UTC): Wed 2022-06-01 05:15:00 UTC
       From now: 1 years 11 months left
[root@testvm1 system]#


Now let's take a look at the description Mon *-05~3, but this time we will ask the program for information about the next 5 times of the timer, which uses the following settings:



[root@testvm1 ~]# systemd-analyze calendar --iterations=5 "Mon *-05~3"
  Original form: Mon *-05~3                
Normalized form: Mon *-05~03 00:00:00      
    Next elapse: Mon 2023-05-29 00:00:00 EDT
       (in UTC): Mon 2023-05-29 04:00:00 UTC
       From now: 2 years 11 months left    
       Iter. #2: Mon 2028-05-29 00:00:00 EDT
       (in UTC): Mon 2028-05-29 04:00:00 UTC
       From now: 7 years 11 months left    
       Iter. #3: Mon 2034-05-29 00:00:00 EDT
       (in UTC): Mon 2034-05-29 04:00:00 UTC
       From now: 13 years 11 months left    
       Iter. #4: Mon 2045-05-29 00:00:00 EDT
       (in UTC): Mon 2045-05-29 04:00:00 UTC
       From now: 24 years 11 months left    
       Iter. #5: Mon 2051-05-29 00:00:00 EDT
       (in UTC): Mon 2051-05-29 04:00:00 UTC
       From now: 30 years 11 months left    
[root@testvm1 ~]#


I believe we've covered enough use cases systemd-analyze calendarto get you started testing your own calendar event definitions. Keep in mind that the tool systemd-analyzehas other interesting features.



Additional materials



There are many publications on systemd on the internet, but they are mostly too short, very simplistic, or even buggy. This article provides some good sources of information on systemd. Below is a list of links to some more quality materials on this topic. 



  • A practical guide to systemd by the Fedora Project.
  • A cheat sheet from the Fedora Project that maps the legacy SystemV and systemd commands.
  • Details about systemd and why it was created.
  • Material with information and advice, dedicated to systemd.
  • (Lennart Poettering), systemd. , 2010 2011, . systemd .
  • systemd.
  • systemd.




Systemd timers can be used to accomplish the same tasks that cron jobs do. But systemd gives you more flexibility in terms of configuring calendar and monotonic timers.



Even though the service configuration files we create during our experiments are usually called using timers, you can call them at any time using a command like systemctl start myMonitor.service. One timer can start several tasks. This can be, for example, Bash scripts and Linux utilities. A service configuration file can be composed so that when it is called, multiple scripts are executed. You can also make the scripts run separately.



If we talk about the coexistence of systemd, cron and at, then I want to note that I have not yet seen any signs that cron or at would be deprecated. I hope they will continue to be supported, since at is at least much easier than systemd to use for scheduling one-off tasks.



What are you using? Systemd timers or cron jobs?






All Articles