Jump to contentJump to page navigation: previous page [access key p]/next page [access key n]
documentation.suse.com / Working with systemd Timers

Working with systemd Timers

Publication Date: 13 Jun 2024
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 how systemd timers work.

REQUIREMENTS

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 regular systemd services, so can be managed with systemctl.

  • 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

  1. 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 tells systemd which application to run.

  2. 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.

  3. 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.

  4. Start the timer:

    > sudo systemctl start helloworld.timer

    Activates the timer for the current session only.

  5. Enable the timer to make sure that it is activated on boot:

    > sudo systemctl enable helloworld.timer

2.2 The example explained

Example 1: The service file
[Unit]
Description="Hello World script"1

[Service]
ExecStart=/usr/local/bin/helloworld.sh2

1

A brief description explaining the service file's purpose.

2

The application to execute.

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.

Example 2: The timer file
[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

1

A brief description explaining the timer file's purpose.

2

Specifies a timer that triggers the service five minutes after the system boot. See Monotonic timers for details.

3

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.

4

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.

5

The service file to execute.

6

The systemd target in which the timer gets activated. For more information on systemd targets, refer to Managing systemd targets with systemctl.

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.timer
Example 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".7

1

The timer's file name and description.

2

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.

3

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).

4

Date and time the timer is triggered next.

5

Name of the service file the timer triggers.

6

Optional line pointing to documentation (for example, man pages). If not available, an empty line is shown (as in this example).

7

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 --allPATTERN

PATTERN must be a name or a shell globbing expression. The operators *, ?, and [] may be used. Refer to man 7 glob for more information on globbing patterns.

List timers matching a certain state:
> sudo systemctl list-timers --state=STATE

STATE takes the following values: active, failed, load, sub. See man systemctl for details.

Example 4: Listing timers

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

1

The point in time when the timer runs next.

2

The time left till the next timer run.

3

The point in time when the timer ran last.

4

Time elapsed since the last timer run.

5

The name of the timer unit.

6

The name of the service the timer activates.

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

1

Day of week. Possible values are Sun, Mon, Tue, Wed, Thu, Fri, Sat. Leave out to ignore the day of the week.

2

Date. Specify month and day by two digits, year by four digits. Each value can be replaced by the wildcard * to match every occurrence.

3

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 activation

    OnActiveSec=50minutes
  • OnBootSec: time after system boot

    OnBootSec=10hours
  • OnStartupSec: time after the service manager is started. For system services, this is almost equal to OnActiveSec. 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 activated

    OnUnitActiveSec=10seconds
  • OnUnitInactiveSec: time after the corresponding service was last deactivated

    OnUnitInactiveSec=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.sh

If the command takes parameters, add them separated by space:

> sudo systemd-run --on-active="2hours" /usr/local/bin/helloworld.sh --language=pt_BR

Transient 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:

  1. Create a script that sends an e-mail.

  2. Create a systemd service file running the e-mail script.

  3. Test the e-mail service file.

  4. 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.

  1. Create the script /usr/local/bin/send_systemd_email.

    1. 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
    2. Make sure the script is executable:

      > sudo chmod 755 /usr/local/bin/send_systemd_email
  2. 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).

  3. Verify the service file and fix the reported issues:

    > systemd-analyze verify /etc/systemd/system/send_email_to_USER@.service

    If the command returns no output, the file has passed the verification successfully.

  4. 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.service

    If 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.

  5. To add an e-mail notification to the service, add an OnFailure option to the Unit 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

    1

    The OnFailure option takes a service as an argument.

    2

    Replace the part of the service unit file name with the login name.

    3

    Specifies the name of the service (helloworld in this example). This name is available in the e-mail service file as %i.

  6. You have successfully set up the failure notification for systemd services.

Tip
Tip: Sending e-mail notifications to multiple users

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 and journalctl 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
Important
Important: User timers only run during an active session

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.

Important
Important: Environment variables are not inherited

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.

  1. Create a service file executing the script. See Example 1, “The service file” for details.

  2. Create a timer file executing the service file. See Example 2, “The timer file” for general instructions.

    1. 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”.

    2. 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:00
    3. Convert variable assignments. The systemd variable assignment must go into the [Service] section. You cannot convert MAILTO this way—refer to the next step for this.

      cron: VARIABLE=VALUE
      systemd: Environment="VARIABLE=VALUE"
    4. 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”.

Example 6: cron to 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:

Example 7: 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 CtrlC.

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:

  1. Create a service file executing the script. See Example 1, “The service file” for details.

  2. Create a timer file executing the service file. See Example 2, “The timer file” for general instructions.

    1. 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”.

    2. 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:00
    3. Convert variable assignments. The systemd variable assignment must go into the [Service] section. You cannot convert MAILTO this way—refer to the next step for this.

      cron: VARIABLE=VALUE
      systemd: Environment="VARIABLE=VALUE"
    4. 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”.

Example 8: cron to 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