An extensive tutorial on everything there is to know about Linux Systemd Timers, including comprehensible examples and explanations of all configuration options
Please contact us if anything is not clearly described, does not work, seems incorrect or if you require support.
This blog post describes how to create, manage and fully understand systemd timers, which are a replacement for classic Cron jobs. Systemd timers allow for a much more fine grained configuration of when or upon which events to execute jobs, scripts, commands or systemd services in various Linux distributions.
This article is written for Linux beginners and advanced users. It aims to be easy to understand. While the manual page man systemd.timer
can be confusing at times, this tutorial contains extensive practical examples and explains all aspects and configuration options of systemd timers in great detail.
OnUnitActiveSec: Since Activation of the Associated Systemd Service
OnUnitInactiveSec: Since Last Stop of the Associated Systemd Service
Configuring Systemd Timers with Randomized Execution Intervals
AccuracySec: How Accurate the Systemd Timer Should Obey the Specified Execution Time
FixedRandomDelay: Save Randomized Execution Time Across Reboots
Systemd Timer Every 5 Seconds, Every few Seconds or Every Second
Starting a Systemd Timer Every Few Seconds - The Better Alternative
Systemd timers are a scheduling mechanism in Linux systems that use the systemd init system, like Debian, Ubuntu, Fedora, RedHat, CentOS, openSUSE, Arch Linux, Orcacle Linux, Linux Mint and many others. Systemd timers provide a more modern and flexible alternative to traditional Cron jobs and offer much finer granularity in regard to timing, as well as dependency management.
Here are the major differences between Systemd Timers and Linux Cron jobs.
The following section describes how to create a systemd timer using the most common usecase for jobs on Linux: periodically executing a simple command or BASH script.
This example shows how to create a very basic and simple systemd timer. First create simple BASH script that writes a string and the current date to /root/timer-test.log
:
cat << 'EOF' > /root/test.sh
#!/bin/bash
echo "I am a test script executing at $(date)" | tee -a /root/test.log
EOF
Make the script executable:
chmod 700 /root/test.sh
Now create a systemd timer unit for the script: this systemd configuration file is the equivalent to what the execution interval of a cronjob used to be, for example */10 * * * *
. The following systemd timer will execute every 10 minutes.
cat << EOF > /etc/systemd/system/test.timer
[Unit]
Description=Start the Systemd service test.service every 10 minutes
[Timer]
OnCalendar=*:0/10:*
Persistent=true
[Install]
WantedBy=timers.target
EOF
The systemd timer above will execute the systemd service using the same naming scheme - a timer named /etc/systemd/system/test.timer
will execute the systemd service /etc/systemd/system/test.service
. Let’s create a systemd service using the same filename, but with a .service extension:
cat << EOF > /etc/systemd/system/test.service
[Unit]
Description=Execute the BASH script /root/test.sh
[Service]
Type=oneshot
ExecStart=/root/test.sh
EOF
After editing the file, you have to execute the following command to make systemd aware of the changes to its configuration. Every time you change a file below /etc/systemd/system/
, you have to execute this command.
systemctl daemon-reload
As common with systemd, newly created units, such as timers, have to be explicitly enabled. Note that you run systemctl start test.timer
, this will execute the timer regardless of what time you configured it to execute!
systemctl enable test.timer
Created symlink /etc/systemd/system/timers.target.wants/test.timer → /etc/systemd/system/test.timer.
To check the status of the new timer:
systemctl status test.timer
● test.timer - Execute the BASH script /root/test.sh every 10 seconds
Loaded: loaded (/etc/systemd/system/test.timer; enabled; preset: enabled)
Active: active (waiting) since Tue 2024-04-16 16:19:51 CEST; 48s ago
Trigger: Tue 2024-04-16 16:20:50 CEST; 9s left
Triggers: ● test.service
If we now look into the /root/test.log file, it shows that the script is executed every 10 seconds:
cat test.log
I am a test script executing at Tue Apr 16 02:23:50 PM UTC 2024
I am a test script executing at Tue Apr 16 02:24:02 PM UTC 2024
I am a test script executing at Tue Apr 16 02:24:13 PM UTC 2024
If we look into the systemd journal (thats where the logs are) we can see information about the timer being executed:
journalctl _COMM=systemd
[...]
Apr 16 14:23:50 p1 systemd[1]: Starting test.service - MyScript Service...
Apr 16 14:23:50 p1 systemd[1]: test.service: Deactivated successfully.
Apr 16 14:23:50 p1 systemd[1]: Finished test.service - MyScript Service.
Apr 16 14:24:02 p1 systemd[1]: Starting test.service - MyScript Service...
Apr 16 14:24:02 p1 systemd[1]: test.service: Deactivated successfully.
Apr 16 14:24:02 p1 systemd[1]: Finished test.service - MyScript Service.
Apr 16 14:24:13 p1 systemd[1]: Starting test.service - MyScript Service...
Apr 16 14:24:13 p1 systemd[1]: test.service: Deactivated successfully.
Apr 16 14:24:13 p1 systemd[1]: Finished test.service - MyScript Service.
[...]
Attentive readers might have noticed that we use tee
in the test.sh bash script, which means that the script is not only writing the output of the echo
command to /root/test.log but also generating output when executed. In order to show the output that was generated by this script, which has been executed by the systemd test.service, use this command:
journalctl _SYSTEMD_UNIT=test.service
Apr 16 14:46:02 p1 test.sh[749373]: I am a test script executing at Tue Apr 16 14:43:31 UTC 2024
Apr 16 14:46:11 p1 test.sh[749420]: I am a test script executing at Tue Apr 16 14:43:31 UTC 2024
Apr 16 14:46:23 p1 test.sh[749467]: I am a test script executing at Tue Apr 16 14:43:31 UTC 2024
To disable a Systemd timer again, use the following commands. Disabling a Systemd timer means preventing it from starting automatically in the future. When you disable a timer, you are essentially removing its association with any target units, making it inactive. However, if the timer is currently running, disabling it does not stop its current execution.
systemctl disable test.timer
Removed "/etc/systemd/system/timers.target.wants/test.timer".
Stopping a systemd timer means halting its current execution if it is running. This action immediately terminates any ongoing execution of the timer. However, stopping a timer does not affect its status as enabled or disabled. Even if you stop a timer, it can still start automatically in the future if it is enabled.
systemctl stop test.timer
# this command does not print output when the timer is successfully stopped
Lets look at /etc/systemd/system/test.timer
and /etc/systemd/system/test.service
that we created above in detail. To view all available information on systemd timers, refer to man systemd.timer
.
With systemd timers, OnCalendar is arguably the most relevant configuration option. It allows for very fine grained specification of when to run a specific timer. As with cron, specifying this correctly is a bit confusing at first. The syntax for OnCalendar is:
OnCalendar=DayOfWeek Year-Month-Day Hour:Minute:Second
To execute a systemd timer one single time at epoch (a common reference point in time in IT), this would be the following. The following timer would execute Thursdays (as in every Thursday) on January first 1970 at 0 hours, 0 minutes and 0 seconds. Note that this statement is exemplary and of course doesn’t make sense in the real world.
OnCalendar=Thu 1970-1-1 0:0:0
As you can see, Thursdays (every Thursday) doesn’t make sense in the constellation above, as we specify a specific date. Hence we can omit the Thu
to get the exact same result. If you omit the DayOfWeek, it either means every day of the week, or as in the following example, it means that day of the week that January 1st is:
OnCalendar=1970-1-1 0:0:0
Systemd-timers are usually not intended to start a systemd service one single time, but periodically (you can of course configure them to start only once). To execute a timers assigned systemd service every Thursday, we can specify:
OnCalendar=Thu
Only specifying Thu
means that the timer will be executed at 00:00:00. If you omit values, the tool systemd-analyze calendar
is very useful to show you a normalized form of the OnCalendar specification that will actually be used:
systemd-analyze calendar "Thu"
Original form: Thu
Normalized form: Thu *-*-* 00:00:00
Next elapse: Thu 2024-04-18 00:00:00 UTC
From now: 1 day 6h left
As we are used to from cron, an asterisk (*) is used to specify all possible values. To have a systemd timer start its systemd service at every 10th minute of every hour (at 0:00:00, 0:10:00, 1:10:00, …, 2:10:00, and so on), this would be:
systemd-analyze calendar "*:0/10:*"
Original form: *:0/10:*
Normalized form: *-*-* *:00/10:*
Next elapse: Tue 2024-04-16 17:40:00 UTC
From now: 8min left
In order to specify a range, you can use two dots, in the following example that would be every day from Monday to Friday at 8pm:
systemd-analyze calendar "Mon..Fri 20:0"
Original form: Mon..Fri 20:0
Normalized form: Mon..Fri *-*-* 20:00:00
Next elapse: Tue 2024-04-16 20:00:00 UTC
From now: 2h 26min left
You can use a comma to specify a list, in this example Mondays, Wednesdays and Fridays at 5am:
systemd-analyze calendar "Mon,Wed,Fri 5:0"
Original form: Mon,Wed,Fri 5:0
Normalized form: Mon,Wed,Fri *-*-* 05:00:00
Next elapse: Wed 2024-04-17 05:00:00 UTC
From now: 11h left
If you run servers in multiple georgiaphical locations, you commonly set your servers timezone to UTC. If you want a systemd timer to run at a specific time in a different timezone, you can specify that too - in this example on saturdays and sundays at 1pm.
systemd-analyze calendar "Sat,Sun 13:0 Europe/Berlin"
Original form: Sat,Sun 13:0 Europe/Berlin
Normalized form: Sat,Sun *-*-* 13:00:00 Europe/Berlin
Next elapse: Sat 2024-04-20 11:00:00 UTC
From now: 3 days left
To list all available timezones:
timedatectl list-timezones
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
[...]
Systemd provide different methods, or categories, of triggering execution of their assigned Systemd services:
Real-time timers use the actual real (wall-clock) time to trigger execution.
Real-time timers are configured using OnCalendar=, as described above.
Monotonic timers activate after a designated duration from a specific event, like system boot or unit activation. The supported units are (N means any number)
For singular values, like 1year, you can omit the s in yearS. The following options exist for configuring monotonic timers:
Starts a timer after it has been activated, as in systemctl activate test.timer
:
OnActiveSec=3hours 10minutes 17seconds
Starts a timer relative to the machine booting:
OnBootSec=2days 50seconds
Start a timer after systemd itself has been started - this is commonly almost equal to OnBootSec=.
OnStartupSec=1minute 10seconds
Start a timer relative to when the timer was last activated:
OnUnitActiveSec=10seconds
This means that after running:
systemctl enable test.timer
The timer will execute 10 seconds later.
Start a timer relative to when the systemd service it is supposted to start last stopped. If we run a script that executes for 5 seconds, then the following example would execute the timer again 45 seconds later:
OnUnitInactiveSec=45seconds
Transient timers are only working while your session is open - as in while you are logged in with the current Linux user. If you ssh to a server and start a transient timer, and then log out again, it will not be executed. You can however execute this:
loginctl enable-linger <username>
to leave the users session active in the background after logging out.
To create transient timers the systemd-run
command is used.
Execute the systemd service test.service
10 minutes from now:
systemd-run --on-active="10minutes" --unit="test.service"
You can also execute a command or script directly. Everything after the command to execute is considered an argument to that command or script:
systemd-run --on-active="55minutes" /root/test.sh
systemd-run --on-active="55minutes" /root/test.sh --arg1 --arg2
The tool systemd-run
accepts the arguments that resemble the arguments a Systemd timer configuration file accepts, like --on-active=
, --on-startup=
, --on-calendar=
, --on-clock-change
and alike. Refer to man systemd-run
for additional information.
The following configuration options can be used to trigger systemd timers upon changes to the clock. I’m not sure how useful this is… But it is part of Systemd Timers.
boolean, default: false
From man systemd.timer
: “When true, the service unit will be triggered when the system clock (CLOCK_REALTIME) jumps relative to the monotonic clock (CLOCK_MONOTONIC)”.
date
. You can adjust it manually or by using NTP.This means that this timer is triggered when a manual update to the system time is made. From the manual page, one might assume that this would also apply to the regular updates that are done via NTP, like by using ntpdate
, ntpd or systemd-timesyncd, however I was not able to reproduce this. The only way I was able to trigger this was by manually force-setting the time, as shown in the following example:
A Systemd timer using OnClockChange=true
as trigger:
[Unit]
Description=Execute the BASH script /root/test.sh
[Timer]
OnClockChange=true
[Install]
WantedBy=timers.target
The systemd service that this timer triggers:
[Unit]
Description=MyScript Service
[Service]
Type=oneshot
ExecStart=/root/test.sh
The BASH script the service executes:
#!/bin/bash
echo "I am a test script executing at $(date)" | tee -a /root/test.log
Disabling NTP for the system:
timedatectl set-ntp false
Manually setting the time:
date -s '2019-10-17 12:00:00'
Do 17. Okt 12:00:00 UTC 2019
The following result shows that the timer was triggered, which started the service, which executed the BASH script:
cat /root/test.log
I am a test script executing at Do 17. Okt 12:00:00 UTC 2019
Re-enabling NTP:
timedatectl set-ntp true
Switching back to NTP managed time also triggered the Systemd timer:
cat /root/test.log
I am a test script executing at Do 17. Okt 12:00:00 UTC 2019
I am a test script executing at Mi 17. Apr 21:02:13 UTC 2024
boolean, default: false
Executes the timer when you set a new timezone:
OnTimezoneChange=true
Change the timezone using the following command to trigger the timer:
timedatectl set-timezone America/New_York
The following configuration options can be used to execute systemd timers at random times within a definable timeframe.
default: 1min
This configuration option is very important to understand if you want to execute a timer at exactly a given time, down to the second.
By default, systemd timers do NOT execute EXACTLY at the time you specified, but within a variance of AccuracySec=, which by default is one minute!
This means that if you set a timer to execute OnCalendar=21:00:00, it might not execute exactly at 21:00:00 but anywhere between 21:00:00 and 21:01:00, unless you adjusted the AccuracySec= setting, which defaults to one minute.
Here is an example: a systemd timer set to execute every 15 seconds:
cat /etc/systemd/system/test.timer
[Unit]
Description=Execute the BASH script /root/test.sh every 15 seconds
[Timer]
OnCalendar=*:*:0/15
[Install]
WantedBy=timers.target
When looking at the logs, we can see that the script is not executed exactly every 15 seconds, but has quite a few additional seconds of variance for each execution:
journalctl -n 0 -f _SYSTEMD_UNIT=test.service
Apr 17 22:02:16 host test.sh[3536562]: I am a test script executing at Mi 17. Apr 22:02:16 UTC 2024
Apr 17 22:02:31 host test.sh[3536572]: I am a test script executing at Mi 17. Apr 22:02:31 UTC 2024
Apr 17 22:02:48 host test.sh[3536576]: I am a test script executing at Mi 17. Apr 22:02:48 UTC 2024
Apr 17 22:03:05 host test.sh[3536657]: I am a test script executing at Mi 17. Apr 22:03:05 UTC 2024
Apr 17 22:03:18 host test.sh[3536695]: I am a test script executing at Mi 17. Apr 22:03:18 UTC 2024
You can “fix this” using AccuracySec=1s
:
cat /etc/systemd/system/test.timer
[Unit]
Description=Execute the BASH script /root/test.sh
[Timer]
OnCalendar=*:*:0/15
AccuracySec=1
[Install]
WantedBy=timers.target
As you can see in the following results, the execution time is perfectly accurate now:
journalctl -n 0 -f _SYSTEMD_UNIT=test.service
Apr 17 22:00:00 host test.sh[3536066]: I am a test script executing at Mi 17. Apr 22:00:00 UTC 2024
Apr 17 22:00:15 host test.sh[3536136]: I am a test script executing at Mi 17. Apr 22:00:15 UTC 2024
Apr 17 22:00:30 host test.sh[3536142]: I am a test script executing at Mi 17. Apr 22:00:30 UTC 2024
Apr 17 22:00:45 host test.sh[3536168]: I am a test script executing at Mi 17. Apr 22:00:45 UTC 2024
Apr 17 22:01:00 host test.sh[3536270]: I am a test script executing at Mi 17. Apr 22:01:00 UTC 2024
The manual page man systemd.timer
states: “To get the best accuracy, set this option to 1us” - I was not able to get better results by settings this to anything lower than 1 second. The reason for this behavior, or for the default of one minute, is, according to the manual page: “in order to optimize power consumption to suppress unnecessary CPU wake-ups”.
If you are migrating from cron this behavior will most likely be very confusing (and remain undetected) for most users. As you most likely run recurring jobs on a server (where you don’t care about the marginally tiny additional power of systemd timers next to your red-hot-glowing database or PHP application), I would recommend to define this setting in all systemd timers you configure and set it to 1 second.
If you have to run programs on multiple machines in your cluster, but you don’t want to run them all at the same time, this option is for you. It will simply randomize the execution of the timer within the given timeframe. The following example will execute the timer every day at a random time between 00:00:00 and 00:10:00.
Note that the Sec in RandomizedDelaySec indeed does stand for seconds - if you specify RandomizedDelaySec=30
it means 30 seconds. You can also specify RandomizedDelaySec=30seconds
, which is equal to just specifying 30, or you can specify RandomizedDelaySec=5minutes
or alike.
OnCalendar=0:0:0
RandomizedDelaySec=10minutes
To clarify the 0:0:0
:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "0:0:0"
Original form: 0:0:0
Normalized form: *-*-* 00:00:00
Next elapse: Fri 1970-01-02 00:00:00 UTC
From now: 23h left
Note that in this constellation, the value of AccuracySec=, which is described above, might also be important. AccuracySec= defaults to one minute. If you define RandomizedDelaySec=5minutes for a systemd timer that is supposed to execute daily at 00:00:00, it will execute between 00:00:00 and 00:06:00. To prevent this, set AccuracySec=1 (one second). The manual (often) recommends to set AccuracySec=1us (one millisecond), which I think does not make sense and acchieves the exact opposite of what AccuracySec= was designed to do - prevent unneccessary CPU wakeups. Refer to Starting a Systemd Timer Every Few Seconds - The Better Alternative for information on how to best execute tasks more often than once a minute.
boolean, default: false
This boolean configuration option specifies if the randomization time is fixed across reboots. If you set this to true, the random interval will always be identical. Settings RandomizedDelaySec=0 disables this. The default is false.
The exact random time that systemd chooses to execute your timer can obviously not be predicted in this blog post. From man systemd.timer
: “The offset depends on the machine ID, user identifier and timer name”. Hence the following example is only meant for better understanding - we may assume that the following example will ALWAYS execute the timer at 00:07:31, even after a reboot.
OnCalendar=daily
RandomizedDelaySec=10minutes
FixedRandomDelay=true
By default a timer /etc/systemd/systemd/test.timer
would activate a systemd service /etc/systemd/system/test.service
. To override this behavior, you can specify the Unit= like so:
Unit=my_test
Which would activate /etc/systemd/system/my_test.service
. I do not recommend deviating from the default by using this option, as it just makes your configuration below /etc/systemd/system/
more confusing.
boolean, default: false
This boolean configuration option only applies to timers configured with OnCalendar=. If a timer was due to trigger during system downtime, it will execute once the system is back online. When set to false, the timer will not be executed once the machine starts. If it missed several executions while the system was shut off or was otherwise not operational, it will only run once.
Setting this to true will save the last execution time to disk. This file is stored in /var/lib/systemd/timers/stamp-test.timer
for a systemd timer file named /etc/systemd/system/test.timer
. The timestamp is not actually stored in the file, but the files modify time will simply be updated. Here are the timestamps for such a file for a Systemd timer with Persistent=true that is configured to execute once every second:
while true; do ls -la --time-style=full-iso /var/lib/systemd/timers/stamp-test.timer; sleep 1; done
-rw-r--r-- 1 root root 0 2024-04-18 00:44:36.096114000 +0000 /var/lib/systemd/timers/stamp-test.timer
-rw-r--r-- 1 root root 0 2024-04-18 00:44:37.831349000 +0000 /var/lib/systemd/timers/stamp-test.timer
-rw-r--r-- 1 root root 0 2024-04-18 00:44:38.831427000 +0000 /var/lib/systemd/timers/stamp-test.timer
-rw-r--r-- 1 root root 0 2024-04-18 00:44:39.831595000 +0000 /var/lib/systemd/timers/stamp-test.timer
You can use systemctl clean --what=state test.timer
to delete this timestamp file. The manual man systemd.timer
recommends that you execute this command before uninstalling a timer that was configured with Persistent=true
. If you only stop and disable a timer, this file will not be removed.
boolean, default: false
This configuration option is less for servers but more for workstations: if your workstation is in sleeping (suspend to disk or suspend to RAM), setting this option to true will cause your machine to wake up and execute the timer. (Note: I have not tested this and I assume this will need some debugging). Also note that it will not put it back into suspend after executing the timer. Additionally note that this only works if the systemd timer is configured as the root user.
If false, the timer uses a monotonic clock named “CLOCK_MONOTONIC”, which is paused during a suspended system. CLOCK_MONOTONIC is started on system boot and counts the time since then. Refer to man clock_gettime
and search for “CLOCK_MONOTONIC” for additional information.
If true, the timer uses the monotonic clock “CLOCK_BOOTTIME”, which is similar to CLOCK_MONOTONIC, except that it continues when the system is suspended. Because of this, systemd timers can be triggered even if the system is suspended. Refer to man clock_getres
and search for “CLOCK_BOOTTIME” for additional information.
boolean, default: true
Systemd timers often speak of “elapsed timers” - this means that the time until the systemd timer is triggered has “elapsed” - meaning that it is now time to activate the systemd service that the systemd timer is configured for.
When set to false, this configuration option will deactivate a systemd timer after its final configured execution.
The difference between an active and an inactive timer is this:
systemctl status test.timer | grep Active
Active: active (waiting) since Wed 2024-04-17 20:32:59 UTC; 8s ago
systemctl stop test.timer
systemctl status test.timer | grep Active
Active: inactive (dead)
In the following example, I used an OnCalendar value that will only trigger the timer a finite amount of times. I tried this with OnCalendar=*:0/1:0
(once per minute) and it did not work - the timer stayed active and kept executing. Hence this option is useful for deactivating a timer that will trigger a limited amount of times and then never again, and after all possible trigger times are over, it will automatically deactivate the systemd timer.
Here is the example systemd timer - it executes a systemd service that runs the BASH script /root/test.sh every 10 seconds from 21:30:00 until 21:30:50.
cat /etc/systemd/system/test.timer
[Unit]
Description=Execute the BASH script /root/test.sh
[Timer]
OnCalendar=2024-04-17 21:30:0/10
RemainAfterElapse=false
[Install]
WantedBy=timers.target
First time showing the timers status at 21:29:53 - it says it will execute in 7 seconds:
systemctl status test.timer
● test.timer - Execute the BASH script /root/test.sh
Loaded: loaded (/etc/systemd/system/test.timer; enabled; preset: enabled)
Active: active (waiting) since Wed 2024-04-17 21:29:50 UTC; 1s ago
Until: Wed 2024-04-17 21:29:50 UTC; 1s ago
Trigger: Wed 2024-04-17 21:30:00 UTC; 7s left
Triggers: ● test.service
I now waited one minute and the timer executed every 10 seconds from 21:30:00 to 21:30:50. After that, it stopped executing as defined in its OnCalendar setting, and also deactivated itself at 21:30:56:
systemctl status test.timer
○ test.timer - Execute the BASH script /root/test.sh
Loaded: loaded (/etc/systemd/system/test.timer; enabled; preset: enabled)
Active: inactive (dead) since Wed 2024-04-17 21:30:56 UTC; 14s ago
Duration: 1min 6.515s
Trigger: n/a
Triggers: ● test.service
Apr 17 21:29:50 host systemd[1]: Started test.timer - Execute the BASH script /root/test.sh.
Apr 17 21:30:56 host systemd[1]: test.timer: Deactivated successfully.
Here are some practical examples for scheduling systemd timers. Please refer to the description above on how to create the systemd timer configuration files for them.
The following section provides examples for the OnCalendar= configuration option. Please refer to the detailed description of how to use OnCalendar= for all information regarding its syntax.
To syntax-check your own configurations of OnCalendar=, you can use the tool systemd-analyze calendar
. All following examples use the command faketime '1970-01-01 00:00:00'
in front of the systemd-analyze calendar
command in order to improve readability when identifying the remaining time for the systemd timer to execute.
If you want to use Systemd timers that execute precicely at a specific second, like at 05:45:00am, also read this section on how to disable +60 seconds of random time added to each timer by default, and how to disable this!
Example:
OnCalendar="*:0/1:0"
Verification:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "*:0/1:0"
Original form: *:0/1:0
Normalized form: *-*-* *:00/1:00
Next elapse: Thu 1970-01-01 00:01:00 UTC
From now: 59s left
If you want to use Systemd timers that execute precicely at a specific second, like at 05:45:00am, also read this section on how to disable +60 seconds of random time added to each timer by default, and how to disable this!
Example:
OnCalendar="*:0/5:0"
Verification:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "*:0/5:0"
Original form: *:0/5:0
Normalized form: *-*-* *:00/5:00
Next elapse: Thu 1970-01-01 00:05:00 UTC
From now: 4min 59s left
Example:
OnCalendar="hourly"
Verification:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "hourly"
Original form: hourly
Normalized form: *-*-* *:00:00
Next elapse: Thu 1970-01-01 01:00:00 UTC
From now: 59min left
Example:
OnCalendar="0/2:0:0"
Verification:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "0/2:0:0"
Original form: 0/2:0:0
Normalized form: *-*-* 00/2:00:00
Next elapse: Thu 1970-01-01 02:00:00 UTC
From now: 1h 59min left
Example:
OnCalendar="daily"
Verification:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "daily"
Original form: Mon..Sun
Normalized form: *-*-* 00:00:00
Next elapse: Fri 1970-01-02 00:00:00 UTC
From now: 23h left
Example:
OnCalendar="Mon..Sun 14:30"
Verification:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "Mon..Sun 14:30"
Original form: Mon..Sun 14:30
Normalized form: *-*-* 14:30:00
Next elapse: Thu 1970-01-01 14:30:00 UTC
From now: 14h left
Specifying “weekly” will execute the timer each Monday at 00:00:00. Example:
OnCalendar="weekly"
Verification:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "weekly"
Original form: weekly
Normalized form: Mon *-*-* 00:00:00
Next elapse: Mon 1970-01-05 00:00:00 UTC
From now: 3 days left
The following example might be more practical, which will execute the timer every tuesday at 8am:
OnCalendar="Tue 8:00"
Verification:
faketime '1970-01-01 00:00:00' systemd-analyze calendar "Tue 8:00"
Original form: Tue 8:00
Normalized form: Tue *-*-* 08:00:00
Next elapse: Tue 1970-01-06 08:00:00 UTC
From now: 5 days left
The best way to start a systemd timer after booting the machine is to use the OnStartupSec=
configration option, which will start the timer within the specified timeframe after systemd itself was first started:
OnBootSec="10 seconds"
This example describes how to periodically restart a systemd service using a systemd timer.
Systemd timers themselves (the .timer files) are only responsible for timing to START systemd services - when a timer reaches its configured time to execute, it will run a systemctl start some.service
.
However you can simply create a oneshot services that executes a systemctl command, like systemctl restart php8.4-fpm.service
. Starting this particular service would then restart another service.
In order to restart a systemd service using a systemd timer, we have to create both a .timer and a .service file - where the .service file contains a single systemctl command to restart the actual service we want to restart.
Here is a complete example that restarts the systemd service php8.4-fpm using a systemd timer every two hours.
Save the following into /etc/systemd/system/restart-php-fpm.timer
:
[Unit]
Description=Trigger the systemd service restart-php-fpm.service every two hours
[Timer]
OnCalendar=*-*-* 0/2:00:00
[Install]
WantedBy=timers.target
And create a systemd service of type OneShot to actually execute the systemctl restart php8.4-fpm.service
command. Save the following content to /etc/systemd/system/restart-php-fpm.service
:
[Unit]
Description=Restart the systemd service php8.4-fpm.service
[Service]
Type=oneshot
ExecStart=/bin/systemctl restart php8.4-fpm.service
To apply the changes to the systemd configuration files below /etc/systemd/system/
:
systemctl daemon-reload
To enable the timer:
systemctl enable restart-php-fpm.timer
Created symlink /etc/systemd/system/timers.target.wants/restart-php-fpm.timer → /etc/systemd/system/restart-php-fpm.timer.
To show the status of the timer:
systemctl status restart-php-fpm.timer
● restart-php-fpm.timer - Trigger the systemd service restart-php-fpm.service every two hours
Loaded: loaded (/etc/systemd/system/restart-php-fpm.timer; enabled; preset: enabled)
Active: active (waiting) since Tue 2024-04-16 22:00:01 UTC; 4ms ago
Until: Tue 2024-04-16 22:00:01 UTC; 4ms ago
Trigger: Wed 2024-04-17 00:00:00 UTC; 1h 59min left
Triggers: ● restart-php-fpm.service
If you want to test the timer, you can “start” it - note that “enable” means to enable the timer, as in make it wait for its execution time, and “start” means to actually run the timer, regardless of what you configured for its execution time using OnCalendar=:
systemctl start restart-php-fpm.timer
# This command does not print output on successful execution
Refer to the debugging section of this blog post for information on how to check the journalctl
logs if the systemd timer executed successfully.
As described in the section below on how to do this better, I do not consider using systemd timers to execute a systemd service more often than once a minute to be a good approach. It however is possible using Systemd timers, and here is a complete description on how to do this correctly and accurately to the second.
When running systemd timers in a second interval, there are several things to consider. First of, as described in the AccuracySec= section, systemd timers do not obey the time you specify, for example using OnCalendar=, down to the second, unless you explicity configure it to do so. The setting AccuracySec= has a default value of one minute, meaning that systemd will execute the timer with a plus one minute variance - if you set a timer for 00:00:00, it will execute between 00:00:00 and 00:01:00.
Down to executing a timer every 15 seconds, this behavior can be “fixed” by setting AccuracySec=1second
. A systemd timer like OnCalendar=*:*:0/15
will then reliably execute every 15 seconds, down to the second. When specifying an execution interval of less than 15 seconds, I have experienced further difficulties while writing this blog post (keep reading for the solution).
The first problem I encountered is that systemd will start to complain if you restart a systemd service to often - as systemd timers simply trigger systemd services, this applies and triggers the following error message. Let’s configure OnCalendar to execute every 5 seconds:
OnCalendar="*:*:0/5"
Resulting error message:
root@host:~# journalctl -f --identifier systemd
Apr 18 00:09:46 host systemd[1]: test.service: Start request repeated too quickly.
Apr 18 00:09:46 host systemd[1]: test.service: Failed with result 'start-limit-hit'.
Apr 18 00:09:46 host systemd[1]: Failed to start test.service - MyScript Service.
According to this stackoverflow post the “default limit is to allow 5 restarts in a 10sec period. If a service goes over that threshold due to the Restart= config option in the service definition, it will not attempt to restart any further.” With starting a service every 5 seconds, we should be below that however. Regardless of this, here is the solution to how to use a systemd timer once a second. From man systemd.unit
:
StartLimitIntervalSec=interval, StartLimitBurst=burst
Configure unit start rate limiting. Units which are started more than burst times within an interval time span are not permitted to start any more.
Use StartLimitIntervalSec= to configure the checking interval and StartLimitBurst= to configure how many starts per interval are allowed.
When configuring the systemd service that we want to start once a second using the the timer like this:
[Unit]
Description=MyScript Service
[Service]
Type=oneshot
ExecStart=/root/test.sh
StartLimitIntervalSec=1
StartLimitBurst=10
I was able to reliably trigger the systemd timer once a second. Here is the timer file I used:
[Unit]
Description=Execute the BASH script /root/test.sh
[Timer]
OnCalendar=*:*:0/1
AccuracySec=1
[Install]
WantedBy=timers.target
Here is the result:
root@host:~# journalctl -f --identifier test.sh
Apr 18 00:32:55 host test.sh[3570468]: I am a test script executing at Do 18. Apr 00:32:55 UTC 2024
Apr 18 00:32:56 host test.sh[3570472]: I am a test script executing at Do 18. Apr 00:32:56 UTC 2024
Apr 18 00:32:57 host test.sh[3570476]: I am a test script executing at Do 18. Apr 00:32:57 UTC 2024
Apr 18 00:32:58 host test.sh[3570480]: I am a test script executing at Do 18. Apr 00:32:58 UTC 2024
Apr 18 00:32:59 host test.sh[3570485]: I am a test script executing at Do 18. Apr 00:32:59 UTC 2024
As you can see the Systemd timer now reliably executes its assigned service once a second without any errors.
Using systemd timers to execute a systemd service every few seconds is, in my opinion, not a good approach - what you most likely want to do is to write is a program or daemon, which executes your task at the desired interval of seconds. The simplest example in a BASH script would be:
cat << EOF > /usr/local/bin/my-daemon.sh
#!/bin/bash
while true; do
echo "Do things"
sleep 5
done
EOF
Make the script executable:
chmod 700 /usr/local/bin/my-daemon.sh
You can set up a systemd service for this like so:
cat << EOF > /etc/systemd/system/my-daemon.service
[Unit]
Description=My Daemon Service
After=syslog.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my-daemon.sh
Restart=always
SyslogIdentifier=my-daemon
[Install]
WantedBy=multi-user.target
EOF
Reload the systemd daemon to apply the changes:
systemctl daemon-reload
And start the service:
systemctl start my-daemon.service
This uses SyslogIdentifier=my-daemon
in the systemd service to set a unique syslog identifier, which we can now use to filter the logs for only this service with journalctl:
journalctl -f --identifier my-daemon
Apr 18 00:00:00 host my-daemon[3564012]: Do things
Apr 18 00:00:05 host my-daemon[3564012]: Do things
Apr 18 00:00:10 host my-daemon[3564012]: Do things
Apr 18 00:00:15 host my-daemon[3564012]: Do things
Apr 18 00:00:20 host my-daemon[3564012]: Do things
Systemd allows you to define dependencies to other Systemd timers or Systemd services using the configuration Options Requires=
, After=
and Wants=
:
Requires=
After=
Wants=
Here is an example with two systemd timer units: test1.timer and test2.timer. We want test2.timer to only start after test1.timer has finished successfully.
The first timer /etc/systemd/system/timer1.timer
is just a simpler timer, configured to start daily at 00:00:00:
# timer1.timer
[Unit]
Description=Timer 1
[Timer]
OnCalendar=0:0:0
Persistent=true
[Install]
WantedBy=timers.target
The second timer is set up to start at 01:00:00. However it also uses Requires=timer1.timer
and After=timer1.timer
.
# timer2.timer
[Unit]
Description=Timer 2
Requires=timer1.timer
After=timer1.timer
[Timer]
OnCalendar=1:0:0
Persistent=true
[Install]
WantedBy=timers.target
This means that:
Here are some informations that will be helpful in debugging Systemd timers.
Systemd timer units can not only be configured as the Linux root user - unprivileged Linux users can use those as well. Here is how to create an example Systemd timer as regular Linux user.
First create a config directory:
mkdir -p ~/.config/systemd/user/
Now create a timer unit configuration file:
cat << EOF > ~/.config/systemd/user/test.timer
[Unit]
Description=Start the non-root user Systemd service test.service every 10 minutes
[Timer]
OnCalendar=*:0/10:*
Persistent=true
[Install]
WantedBy=timers.target
EOF
Now create a Systemd service file:
cat << EOF > ~/.config/systemd/user/test.service
[Unit]
Description=Execute the BASH script /root/test.sh
[Service]
Type=oneshot
ExecStart=/bin/echo teststring
EOF
To activate the timer first reload systemd to accept the new configuration:
systemctl --user daemon-reload
And then enable the timer:
systemctl --user enable test.timer
Important: User timers only run during an active session! To keep Systemd timers running after logging out, use this:
sudo loginctl enable-linger <user>
As Systemd timers only start Systemd services, you can simply configure the systemd service that the timer will start to run as a specific user and group:
[Unit]
Description=Your Service
[Service]
Type=simple
ExecStart=/path/to/your/command
User=your_username
Group=your_groupname
[Install]
WantedBy=multi-user.target
To show more iterations in systemd-analyze calendar
, use --iterations=N
:
systemd-analyze calendar "Sat,Sun 13:0 Europe/Berlin" --iterations=4
Original form: Sat,Sun 13:0 Europe/Berlin
Normalized form: Sat,Sun *-*-* 13:00:00 Europe/Berlin
Next elapse: Sat 2024-04-20 11:00:00 UTC
From now: 3 days left
Iter. #2: Sun 2024-04-21 11:00:00 UTC
From now: 4 days left
Iter. #3: Sat 2024-04-27 11:00:00 UTC
From now: 1 week 3 days left
Iter. #4: Sun 2024-04-28 11:00:00 UTC
From now: 1 week 4 days left
To improve readability with systemd-analyze calendar
, you can use faketime
to fake a specific time for all following commands:
faketime 'last Friday 5 pm' systemd-analyze calendar "Sat,Sun 13:0 Europe/Berlin"
Original form: Sat,Sun 13:0 Europe/Berlin
Normalized form: Sat,Sun *-*-* 13:00:00 Europe/Berlin
Next elapse: Sat 2024-04-13 11:00:00 UTC
From now: 17h left
See man faketime
for additional information.
Here is some information that might help you to debug your Systemd timers.
When activating a timer that contains non-critical errors, systemd silently disregards them! For instance:
[Unit]
Description=Start the Systemd service test.service every 10 minutes
[Timer]
OnClendar=*:0/10:* <== NOTE THE MISSING a IN Clendar
Persistent=true
[Install]
WantedBy=timers.target
Make sure to verify your Systemd unit files with this command:
systemd-analyze verify /etc/systemd/system/test.timer
/etc/systemd/system/test.timer:5: Unknown key name 'OnClendar' in section 'Timer', ignoring.
test.timer: Timer unit lacks value setting. Refusing.
Unit test.timer has a bad unit file setting.
Example journalctl
logs:
journalctl -f
Apr 20 00:54:42 host systemd[1]: /etc/systemd/system/test.timer:5: Unknown key name 'OnClendar' in section 'Timer', ignoring.
Apr 20 00:54:42 host systemd[1]: test.timer: Timer unit lacks value setting. Refusing.
To list all configured and active Systemd timers:
systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sat 2024-04-20 02:51:31 UTC 2h 2min left Fri 2024-04-19 11:48:43 UTC 13h ago plocate-updatedb.timer plocate-updatedb.service
Sat 2024-04-20 06:59:18 UTC 6h left Fri 2024-04-19 11:48:43 UTC 13h ago apt-daily-upgrade.timer apt-daily-upgrade.service
Sat 2024-04-20 07:33:29 UTC 6h left Sat 2024-04-20 00:24:10 UTC 25min ago anacron.timer anacron.service
[...]
To also list inactive timers:
systemctl list-timers --all
NEXT LEFT LAST PASSED UNIT ACTIVATES
[...]
- - - - apport-autoreport.timer apport-autoreport.service
- - - - restart-php-fpm.timer
- - - - snapd.snap-repair.timer snapd.snap-repair.service
To list all Systemd timers matching a specific wildcard:
systemctl list-timers apt*
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sat 2024-04-20 06:59:18 UTC 6h left Fri 2024-04-19 11:48:43 UTC 13h ago apt-daily-upgrade.timer apt-daily-upgrade.service
Sat 2024-04-20 10:20:51 UTC 9h left Fri 2024-04-19 20:32:56 UTC 4h 17min ago apt-daily.timer apt-daily.service
2 timers listed.
Unlike with Systemd services, which offer a SyslogIdentifier=
setting, timers themselves do not have such a setting and will always log using the “syslog” identifier. To view all logs from the journal that match the syslog field:
journalctl -f _COMM=systemd
Are you looking for
Linux Emergency Support,
Linux Consulting for Projects,
Linux Managed Hosting,
Qubes OS Consulting and Support or
Linux Trainings and Workshops?