Working with systemd
Timers
- WHAT?
From running a backup script at regular intervals to starting a specific process as soon as the machine boots, there are plenty of tasks that require scheduling on a Linux system.
systemd
timers provide a flexible mechanism for scheduling and managing jobs and services.- WHY?
This article is intended to provide a complete overview of
systemd
timers covering creating, maintaining, testing, troubleshooting and migrating from cron.- EFFORT
It takes 10 minutes to create an example
systemd
timer. You need up to 30 minutes to fully understand howsystemd
timers work.- REQUIREMENTS
Basic understanding of
systemd
.root
or sudo privileges. To usesystemd
timers as a regular user, refer to Section 7, “Using timers as a regular user” first.
1 The systemd
timer concept #
systemd
timer units provide a mechanism for scheduling jobs on Linux. The execution time
of these jobs can be based on the time and date or on events.
systemd
timer units are identified by the .timer
file
name extension. Each timer file requires a corresponding service file it controls. In other
words, a timer file activates and manages the corresponding service file. systemd
timers
support the following features:
Jobs scheduled using a timer unit can depend on other
systemd
services. Timer units are treated as regularsystemd
services, so can be managed withsystemctl
.Timers can be real-time (being triggered on calendar events) or monotonic (being triggered at a specified time elapsed from a certain starting point).
Time units are logged to the system journal, which makes it easier to monitor and troubleshoot them.
Timers use the centralized
systemd
management services.If the system is off during the expected execution time, the timer is executed once the system is running again.
2 Creating a timer #
The following example shows how to set up a timer that triggers the
helloworld.sh
shell script after boot time and repeats its execution
every 24 hours relative to its activation time. It also runs Monday to Friday at 10 a.m.
2.1 Hello World example #
Create the file
/etc/systemd/system/helloworld.service
with the following content:[Unit] Description="Hello World script" [Service] ExecStart=/usr/local/bin/helloworld.sh
This is a
systemd
service file that tellssystemd
which application to run.Create the file
/etc/systemd/system/helloworld.timer
with the following content:[Unit] Description="Run helloworld.service 5min after boot and every 24 hours relative to activation time" [Timer] OnBootSec=5min OnUnitActiveSec=24h OnCalendar=Mon..Fri *-*-* 10:00:* Unit=helloworld.service [Install] WantedBy=multi-user.target
This is the timer file that controls the activation of the respective service file.
Verify that the files you created above contain no errors:
>
systemd-analyze verify /etc/systemd/system/helloworld.*If the command returns no output, the files have passed the verification successfully.
Start the timer:
>
sudo
systemctl start helloworld.timerActivates the timer for the current session only.
Enable the timer to make sure that it is activated on boot:
>
sudo
systemctl enable helloworld.timer
2.2 The example explained #
[Unit] Description="Hello World script"1 [Service] ExecStart=/usr/local/bin/helloworld.sh2
The [Unit]
and [Service]
sections are the minimum
sections required for a service file to work. systemd
service files normally contain an
[Install]
section that determines one or more targets for a service to
load. This section is not required in service files for timers, since this information is
provided with the timer file. For advanced configuration, refer to
Managing
systemd
targets with systemctl.
[Unit] Description="Run helloworld.service 5min after boot and every 24 hours relative to activation time"1 [Timer] OnBootSec=5min2 OnUnitActiveSec=24h3 OnCalendar=Mon..Fri *-*-* 10:00:*4 Unit=helloworld.service5 [Install] WantedBy=multi-user.target6
A brief description explaining the timer file's purpose. | |
Specifies a timer that triggers the service five minutes after the system boot. See Monotonic timers for details. | |
Specifies a timer that triggers the service 24 hours after the service has been activated (that is, the timer triggers the service once a day). See Real-time timer for details. | |
Specifies a timer that triggers the service at fixed points in time (in this example, Monday to Friday at 10 a.m.). See Real-time timer for details. | |
The service file to execute. | |
The |
3 Managing timers #
You can manage timers using the systemctl
command.
- Starting and stopping timers
>
sudo
systemctl start TIMER.timer>
sudo
systemctl restart TIMER.timer>
sudo
systemctl stop TIMER.timer- Enabling and disabling timers
>
sudo
systemctl enable TIMER.timer>
sudo
systemctl disable TIMER.timer- Showing the timer file contents
>
sudo
systemctl cat TIMER.timer- Checking on a specific timer
>
sudo
systemctl status TIMER.timerExample 3: Timer Status #>
sudo
systemctl status helloworld.timer ● helloworld.timer - "Run helloworld.service 5min after boot and every 24 hours relative to activation time"1 Loaded: loaded (/etc/systemd/system/helloworld.timer; disabled; vendor preset: disabled)2 Active: active (waiting) since Tue 2022-10-26 18:35:41 CEST; 6s ago3 Trigger: Wed 2022-10-27 18:35:41 CEST; 23h left4 Triggers: ● helloworld.service5 6 Oct 26 18:35:41 neo systemd[1]: Started "Run helloworld.service 5min after boot and every 24 hours relative to activation time".7The timer's file name and description.
Lists whether a timer has been successfully parsed and is kept in memory (loaded), shows the full path to the timer file, and shows whether the timer is being started at boot time (enabled) or not (disabled). The first value shows the current system configuration, the second value the vendor preset.
Indicates whether the timer is active (waiting to trigger events) or inactive. If active, it also shows the time that has passed since the last activation (6 seconds in this example).
Date and time the timer is triggered next.
Name of the service file the timer triggers.
Optional line pointing to documentation (for example, man pages). If not available, an empty line is shown (as in this example).
Latest journal entry created by the timer.
To list all timers available on the system, use systemctl list-timers
. The
following options are available:
- List all active timers:
>
sudo
systemctl list-timers- List all timers including inactive ones:
>
sudo
systemctl list-timers --all- List all timers matching a pattern:
>
sudo
systemctl list-timers PATTERN>
sudo
systemctl list-timers --allPATTERNPATTERN must be a name or a shell globbing expression. The operators
*
,?
, and[]
may be used. Refer toman 7 glob
for more information on globbing patterns.- List timers matching a certain state:
>
sudo
systemctl list-timers --state=STATESTATE takes the following values:
active
,failed
,load
,sub
. Seeman systemctl
for details.
Running any systemctl list-timers
results in a table similar to the
one below. In this example, all active timers matching the pattern
snapper*
are listed:
>
sudo
systemctl list-timers snapper* NEXT1 LEFT2 LAST3 PASSED4 UNIT5 ACTIVATES6 ----------------------------------------------------------------------------------------------------------------------------- Tue 2022-10-26 19:00:00 CEST 39min left Tue 2022-10-26 18:00:29 CEST 19min ago snapper-timeline.timer snapper-timeline.service Wed 2022-10-27 08:33:04 CEST 14h left Tue 2022-10-26 08:33:04 CEST 9h ago snapper-cleanup.timer snapper-cleanup.service
4 Timer types #
systemd
supports two types of timers: real-time (based on calendar) and monotonic (based
on events). Although timers are normally persistent, systemd
also allows to set up
transient timers that are only valid for the current session.
- Real-time timer
Real-time timers are triggered by calendar events. They are defined using the option
OnCalendar
.You can specify when to trigger an event based on date and time. Use the following template:
OnCalendar=DayOfWeek1 Year-Month-Day2 Hour:Minute:Second3
Day of week. Possible values are
Sun
,Mon
,Tue
,Wed
,Thu
,Fri
,Sat
. Leave out to ignore the day of the week.Date. Specify month and day by two digits, year by four digits. Each value can be replaced by the wildcard
*
to match every occurrence.Time. Specify each value by two digits. Each value can be replaced by the wildcard
*
to match every occurrence.Applies to all values: Use two dots to define a continuous range (
Mon..Fri
). Use a comma to delimit a list of separate values (Mon,Wed,Fri
).Example 5: Real-time timer examples #6 p.m. every Friday:
OnCalendar=Fri *-*-* 18:00:00
5 a.m. every day:
OnCalendar=Mon..Sun *-*-* 5:00:00
1 a.m. and 3 a.m. on Sundays and Tuesdays:
OnCalendar=Tue,Sun *-*-* 01,03:00:00
Single date:
OnCalendar=Mo..Sun 2023-09-23 00:00:01
To specify triggers at different times, you can create more than one OnCalendar entry in a single timer file:
OnCalendar=Mon..Fri *-*-* 10:00 OnCalendar=Sat,Sun *-*-* 22:00
For a full list of available features and options, refer to
man 7 systemd.time
that offers additional information on the following topics:shorten the syntax and use abbreviations
specify repetitions
find specific days in a month (last day of month, last Sunday, etc.)
apply time zones
- Monotonic timers
Monotonic timers are triggered at a specified time elapsed from a certain event, such as a system boot or system unit activation event. Values are defined as time units (minutes, hours, days, months, years, etc.). The following units are supported:
usec
,msec
,seconds
,minutes
,hours
,days
,weeks
,months
,years
. There are several options for defining monotonic timers:OnActiveSec
: time after unit activationOnActiveSec=50minutes
OnBootSec
: time after system bootOnBootSec=10hours
OnStartupSec
: time after the service manager is started. For system services, this is almost equal toOnActiveSec
. Use this for user services where the service manager is started at user login.OnStartupSec=5minutes 20seconds
OnUnitActiveSec
: time after the corresponding service was last activatedOnUnitActiveSec=10seconds
OnUnitInactiveSec
: time after the corresponding service was last deactivatedOnUnitInactiveSec=2hours 15minutes 18 seconds
- Transient timers
Transient timers are temporary timers that are only valid for the current session. Using these timers, you can either use an existing service file or start a program directly. Transient timers are invoked by running
systemd-run
.The following example runs the
helloworld.service
unit every two hours:>
sudo
systemd-run --on-active="2hours" --unit="helloworld.service"To run a command directly, use the following syntax. In this example, the script
/usr/local/bin/helloworld.sh
is called directly:>
sudo
systemd-run --on-active="2hours" /usr/local/bin/helloworld.shIf the command takes parameters, add them separated by space:
>
sudo
systemd-run --on-active="2hours" /usr/local/bin/helloworld.sh --language=pt_BRTransient timers can be monotonic or real-time. The following switches are supported and work as described in Monotonic timers:
--on-active
--on-startup
--on-unit-active
--on-unit-inactive
--on-calendar
For more information, refer to
man 1 systemd-run
.
5 Testing calendar entries #
systemd
provides a tool for testing and creating calendar timer entries for real-time
timers: systemd-analyze calendar
. It accepts the same argument as the
OnCalendar
entry required to set up real-time timers.
You can concatenate several arguments separated by space. If the term to test is correct, the
output shows you when the timer is triggered next (in local time and UTC). It also
shows the string in Normalized form
, and it is recommended to use that string
in the timer file. Consider the following examples:
>
systemd-analyze calendar "Tue,Sun *-*-* 01,03:00:00" Normalized form: Tue,Sun *-*-* 01,03:00:00 Next elapse: Sun 2021-10-31 01:00:00 CEST (in UTC): Sat 2021-10-30 23:00:00 UTC From now: 3 days left>
systemd-analyze calendar "Mon..Fri *-*-* 10:00" "Sat,Sun *-*-* 22:00" Original form: Mon..Fri *-*-* 10:00 Normalized form: Mon..Fri *-*-* 10:00:00 Next elapse: Thu 2021-10-28 10:00:00 CEST (in UTC): Thu 2021-10-28 08:00:00 UTC From now: 19h left Original form: Sat,Sun *-*-* 22:00 Normalized form: Sat,Sun *-*-* 22:00:00 Next elapse: Sat 2021-10-30 22:00:00 CEST (in UTC): Sat 2021-10-30 20:00:00 UTC From now: 3 days left
For recurring timers, use the –iterations N
switch
to list trigger times, then test whether they work as expected. The argument
N specifies the number of iterations you would like to test. The
following example string triggers every 8 hours (starting at 00:00:00) on Sundays:
>
systemd-analyze calendar --iterations 5 "Sun *-*-* 0/08:00:00"
Original form: Sun *-*-* 0/08:00:00
Normalized form: Sun *-*-* 00/8:00:00
Next elapse: Sun 2021-10-31 00:00:00 CEST
(in UTC): Sat 2021-10-30 22:00:00 UTC
From now: 3 days left
Iter. #2: Sun 2021-10-31 08:00:00 CET
(in UTC): Sun 2021-10-31 07:00:00 UTC
From now: 3 days left
Iter. #3: Sun 2021-10-31 16:00:00 CET
(in UTC): Sun 2021-10-31 15:00:00 UTC
From now: 4 days left
Iter. #4: Sun 2021-11-07 00:00:00 CET
(in UTC): Sat 2021-11-06 23:00:00 UTC
From now: 1 week 3 days left
Iter. #5: Sun 2021-11-07 08:00:00 CET
(in UTC): Sun 2021-11-07 07:00:00 UTC
From now: 1 week 3 days left
6 Getting e-mail notifications when a timer fails #
systemd
does not offer a feature similar to cron's MAILTO. The procedure below
describes a workaround to enable e-mail notifications when a timer fails.
The procedure consists of the following steps:
Create a script that sends an e-mail.
Create a
systemd
service file running the e-mail script.Test the e-mail service file.
From the service that the timer controls, call the created e-mail service file via
OnFailure
.
In the following example, we are using the mailx
command from package
mailx. It requires the Postfix e-mail server to be installed and correctly
configured.
Create the script
/usr/local/bin/send_systemd_email
.The script requires two parameters:
$1
, the e-mail address, and$2
, the name of the service file for which the failure notification is received. Both parameters are supplied by the unit file running the mail script.#!/bin/sh systemctl status --full "$2" | mailx -S sendwait\ -s "Service failure for $2" -r root@$HOSTNAME $1
Make sure the script is executable:
>
sudo
chmod 755 /usr/local/bin/send_systemd_email
Create the file
/etc/systemd/system/send_email_to_USER@.service
.[Unit] Description=Send systemd status information by email for %i to USER [Service] Type=oneshot ExecStart=/usr/local/bin/send_systemd_email EMAIL_ADDRESS %i User=root Group=systemd-journal
Replace USER and EMAIL_ADDRESS in the file with the login and e-mail address of the user that should receive the e-mail.
%i
is the name of the service that has failed (it is passed on to the e-mail service by the%n
parameter).Verify the service file and fix the reported issues:
>
systemd-analyze verify /etc/systemd/system/send_email_to_USER@.serviceIf the command returns no output, the file has passed the verification successfully.
To verify the complete procedure, start the service using the
dbus
instance for testing. (You can use any other service that is currently running. dbus is used in this example because the service is guaranteed to run on any installation.)>
sudo
systemctl start send_email_to_USER@dbus.serviceIf successful, EMAIL_ADDRESS receives an e-mail with the subject
Service failure for dbus
containing dbus status messages in the body. (This is just a test, there is no problem with the dbus service. You can safely delete the e-mail, no action is required).If the test e-mail has been successfully sent, proceed by integrating it into your service file.
To add an e-mail notification to the service, add an
OnFailure
option to theUnit
section of the service file for which you would like to get notified in the event of failure:[Unit] Description="Hello World script" OnFailure1=send_email_to_USER2@%n3.service [Service] ExecStart=/usr/local/bin/helloworld.sh
You have successfully set up the failure notification for systemd
services.
The e-mail service file has the recipient's e-mail address hard-coded. To send notification e-mails to a different user, copy the e-mail service file, and replace the user login in the file name and the e-mail address within the copy.
To send a failure notification to several recipients simultaneously, add the respective service files to the service file (use spaces as a separator):
OnFailure=send_email_to_tux@%n.service send_email_to_wilber@%n.service
7 Using timers as a regular user #
systemd
timers can also be used by regular users. It helps you to automate recurring
tasks like backups, processing images, or moving data to the cloud.
The same procedures and tasks as for system-wide timers are valid. However, the following differences apply:
Timer and service files must be placed in
~/.config/systemd/user/
.All
systemctl
andjournalctl
commands must be run with the--user
switch.systemd-analyze
does not require this option.As a regular user, you must provide the path to the unit files, as in the examples below. Otherwise, if a system-wide timer with the same name exists, it would be executed or listed instead.
>
systemctl --user start ~/.config/systemd/user/helloworld.timer>
systemctl --user enable ~/.config/systemd/user/helloworld.timer>
systemctl --user list-timers>
journalctl --user -u helloworld.*>
systemd-analyze verify ~/.config/systemd/user/helloworld.timer
As with other systemd
services started as a regular user, user timers only run when the
user is logged in. Instead, to start user timers at boot time and keep them running after
logout, enable lingering for each affected user:
sudo loginctl enable-linger USER
For more information, refer to man 1 loginctl
.
The systemd
user instance does not inherit environment variables set by scripts like
~/.profile
or ~/.bashrc
. To check the systemd
environment, run systemctl --user show-environment
.
To import any variables missing in the systemd
environment, specify the following command
at the end of your ~/.bashrc
:
systemctl --user import-environment VARIABLE1 VARIABLE2
8 Migrating from cron to systemd
timers #
All cron jobs can be migrated to systemd
timers. Find
instructions and an example here.
Create a service file executing the script. See Example 1, “The service file” for details.
Create a timer file executing the service file. See Example 2, “The timer file” for general instructions.
Convert calendar entries. Time is specified differently in cron and
systemd
. Use the patterns below as a conversion template:Cron: Minute Hour Day Month DayOfWeek systemd: OnCalendar=DayOfWeek Year-Month-Day Hour:Minute:Second
To test the converted calendar entry, follow the instructions in Section 5, “Testing calendar entries”.
Convert cron nicknames (
@NICK
):Cron :
systemd
timer -------- : ---------------------------- @reboot : OnBootSec=1s @yearly : OnCalendar=*-01-01 00:00:00 @annually: OnCalendar=*-01-01 00:00:00 @monthly : OnCalendar=*-*-01 00:00:00 @weekly : OnCalendar=Sun *-*-* 00:00:00 @daily : OnCalendar=*-*-* 00:00:00 @hourly : OnCalendar=*-*-* *:00:00Convert variable assignments. The
systemd
variable assignment must go into the[Service]
section. You cannot convertMAILTO
this way—refer to the next step for this.cron: VARIABLE=VALUE systemd: Environment="VARIABLE=VALUE"
Set up e-mail notifications to replace cron's MAILTO feature by following the instructions in Section 6, “Getting e-mail notifications when a timer fails”.
systemd
timer migration #
Here are the crontab entries which call the script helloworld.sh
5
minutes after booting and at 10 o'clock each Monday to Friday:
@reboot sleep 300 && /usr/local/bin/helloworld.sh 0 10 * * * 1-5 /usr/local/bin/helloworld.sh
The systemd
service file (helloworld.service
) calling the script
looks like this:
[Unit] Description="Hello World script" [Service] ExecStart=/usr/local/bin/helloworld.sh
The timer file (helloworld.timer
) looks like this:
[Unit] Description="Run helloworld.service 5min after boot and at 10am every Mon-Fri" [Timer] OnBootSec=5min OnCalendar=Mon..Fri *-*-* 10:00:* Unit=helloworld.service [Install] WantedBy=multi-user.target
9 Troubleshooting and FAQs #
Learn how to debug and troubleshoot systemd
timers that have failed. Find answers to
frequently asked questions on systemd
timers.
9.1 Avoiding errors #
To avoid errors with systemd
timers, make sure to follow these best practices:
Verify that the executable you specify in the service with
ExecStart
runs correctly.Check the syntax of the service and timer files by running
systemd-analyze verify FILE
.Check execution times of calendar entries by running
systemd-analyze calendar CALENDER_ENTRY
.
9.2 Event is not triggered #
When you activate a timer that contains non-critical errors, systemd
silently ignores them.
For example:
systemd
timer file cutout containing a non-fatal error #[Timer] OnBootSec=5min OnClendar=Mon..Fri 10:00 Unit=helloworld.service
Line 3 contains a syntax error (OnClendar
instead of
OnCalendar
). Since the [Timer]
section contains a
second timer entry (OnBoot), the error is not critical and is silently ignored. As a
consequence, the Monday to Friday trigger is not executed. The only way to detect the
error is to use the command systemd-analyze verify
:
#
systemd-analyze verify /etc/systemd/system/helloworld.timer
/etc/systemd/system/helloworld.timer:7: Unknown key name 'OnClendar' in section 'Timer', ignoring.
9.3 Checking the system journal for errors #
As with every systemd
service, events and actions triggered by timers are logged with the
system journal. If a trigger does not behave as expected, check the log messages with
journalctl
. To filter the journal for relevant information, use the
-u
switch to specify the systemd
timers and service files. Use this option
to show the log entries for the timer and the corresponding service
file:
sudo journalctl -u helloworld.timer -u helloworld.service
or shorter (if applicable):
sudo journalctl -u helloworld.*
journalctl
is a tool that supports many options and filters. Please refer
to man 1 journalctl
for in-depth information. The following options are
useful for troubleshooting timers:
-b
: Only show entries for the current boot.-S today
: Only show entries from today.-x
: Show help texts alongside the log entry.-f
: Start with the most recent entries and continuously print the log as new entries get added. Useful to check triggers that occur in short intervals. Exit with Ctrl–C.
9.4 systemd
timer: catching up on missed runs #
If a systemd
timer was inactive or the system was off during the
expected execution time, missed events can optionally be triggered
immediately when the timer is activated again. To enable this, add the
configuration option Persistent=true
to the
[Timer]
section:
[Timer] OnCalendar=Mon..Fri 10:00 Persistent=true Unit=helloworld.service
9.5 How to migrate from cron to systemd
timers? #
All cron jobs can be migrated to systemd
timers. Here are general instructions on migrating
a cron job:
Create a service file executing the script. See Example 1, “The service file” for details.
Create a timer file executing the service file. See Example 2, “The timer file” for general instructions.
Convert calendar entries. Time is specified differently in cron and
systemd
. Use the patterns below as a conversion template:Cron: Minute Hour Day Month DayOfWeek systemd: OnCalendar=DayOfWeek Year-Month-Day Hour:Minute:Second
To test the converted calendar entry, follow the instructions in Section 5, “Testing calendar entries”.
Convert cron nicknames (
@NICK
):Cron :
systemd
timer -------- : ---------------------------- @reboot : OnBootSec=1s @yearly : OnCalendar=*-01-01 00:00:00 @annually: OnCalendar=*-01-01 00:00:00 @monthly : OnCalendar=*-*-01 00:00:00 @weekly : OnCalendar=Sun *-*-* 00:00:00 @daily : OnCalendar=*-*-* 00:00:00 @hourly : OnCalendar=*-*-* *:00:00Convert variable assignments. The
systemd
variable assignment must go into the[Service]
section. You cannot convertMAILTO
this way—refer to the next step for this.cron: VARIABLE=VALUE systemd: Environment="VARIABLE=VALUE"
Set up e-mail notifications to replace cron's MAILTO feature by following the instructions in Section 6, “Getting e-mail notifications when a timer fails”.
systemd
timer migration #
Here are the crontab entries which call the script helloworld.sh
5
minutes after booting and at 10 o'clock each Monday to Friday:
@reboot sleep 300 && /usr/local/bin/helloworld.sh 0 10 * * * 1-5 /usr/local/bin/helloworld.sh
The systemd
service file (helloworld.service
) calling the script
looks like this:
[Unit] Description="Hello World script" [Service] ExecStart=/usr/local/bin/helloworld.sh
The timer file (helloworld.timer
) looks like this:
[Unit] Description="Run helloworld.service 5min after boot and at 10am every Mon-Fri" [Timer] OnBootSec=5min OnCalendar=Mon..Fri *-*-* 10:00:* Unit=helloworld.service [Install] WantedBy=multi-user.target
10 For more information #
For a full reference on
systemd
timers including advanced configuration options (like delays or handling clock or time zone changes), refer toman 5 systemd.timer
.
11 Legal Notice #
Copyright© 2006–2024 SUSE LLC and contributors. All rights reserved.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or (at your option) version 1.3; with the Invariant Section being this copyright notice and license. A copy of the license version 1.2 is included in the section entitled “GNU Free Documentation License”.
For SUSE trademarks, see https://www.suse.com/company/legal/. All other third-party trademarks are the property of their respective owners. Trademark symbols (®, ™ etc.) denote trademarks of SUSE and its affiliates. Asterisks (*) denote third-party trademarks.
All information found in this book has been compiled with utmost attention to detail. However, this does not guarantee complete accuracy. Neither SUSE LLC, its affiliates, the authors, nor the translators shall be held liable for possible errors or the consequences thereof.