Skip to content

systemd Timers guide

Timers are a built-in systemd mechanism for scheduling tasks.

Each timer is a systemd unit with a .timer extension that triggers an associated .service unit.

In practice, each *.timer file must point to a *.service file describing what should be executed.

systemd timers offer:

  • calendar schedules (OnCalendar) and monotonic schedules (e.g., delays after system startup)
  • event logging in journald
  • defining dependencies between services

If the server (or PC) was turned off at the time of invocation, the timer will make up for the service call after restart (with Persistent enabled), which in cron requires additional tools (cronrun/anacron)


Comparison with traditional cron

Cron is a classic task scheduling program. It's simple to use but limited. Timers offer greater flexibility and system integration:

Advanced time options

systemd timers can operate on a calendar basis (similar to cron) and monotonically - e.g., after a specified time from system startup or after another service ends.

This allows you to, for example, start a task a few minutes after system startup (OnBootSec=5min) or cyclically repeat a task every 6 hours after it finishes (OnUnitActiveSec=6h). Cron doesn't handle such dependencies or events.

Logging and monitoring

Each timer invocation (and the service it runs) is logged in journald. You can easily check the history of calls and errors with a single command, e.g., journalctl -u custom.timer -u custom.service.

In cron, results must be collected manually (e.g., by redirecting logs), and tasks from different users are scattered across separate crontabs. systemd allows you to list all timers with one command:

systemctl list-timers --all

... giving you a complete overview of scheduled tasks (as opposed to searching through multiple crontab files).

Service management

Timers are systemd units, so you can assign resource control parameters, priorities, or systemcall filters to them. It might require adding dependencies - e.g., authentication or ensuring service B is running before performing a backup.

In the service or timer file, you can define Requires= and After=, guaranteeing the order of execution. Cron doesn't offer such control.

Systemcall filters in systemd (.service)

SystemCallFilter filters in systemd restrict what a given process can execute at the Linux kernel level. They allow you to specify allowed or forbidden system calls (syscalls), such as open(), mount(), or execve().

This lets you:

  • protect the system from logical errors or vulnerabilities in applications,
  • secure a service against potential malicious activity,
  • reduce the attack vector (hardening),
  • enforce sandboxing for processes launched by systemd.

These filters are defined in the .service file, e.g.:

[Service]
ExecStart=/usr/local/bin/imascript.sh
SystemCallFilter=~@mount @reboot @module

A timer (.timer) can run a .service that has such safeguards - so scheduled tasks also operate in a limited, controlled environment.

Security and permissions

For system tasks, cron typically uses a single context (e.g., root or user), while systemd timers can function as system services or user-level services. You can also easily restrict access rights (e.g., using User= in [Service]).


Characteristics of cron

Cron is simple and well-known, doesn't require systemd, so it also works on systems without it.

It also has simpler configuration ("one line" instead of two files).

It is less flexible:

  • No support for events (only fixed calendar schedule)
  • No easy monitoring mechanisms
  • No task recovery mechanisms
  • No resource control

Note

systemd timers are a more modern alternative to cron, especially when we need better control, logging, and flexibility. For simple, infrequent tasks, cron is still sufficient, but in many scenarios, systemd is more convenient.


Creating and configuring timers

To use a timer, you need two units: a .service file (describing the task) and a .timer file (describing the schedule). How to create a timer:

1. Service file (.service)

Defines the command to execute. Typical structure:

[Unit]
Description=Description 

[Service]
ExecStart=/path/to/binary
Type=oneshot            # (optional, e.g., for one-time scripts)

In ExecStart, use full paths and make sure the program works correctly

2. Timer file (.timer)

Specifies when the associated service should be launched. Basic sections:

[Unit]
Description=Task X timer

[Timer]
OnCalendar=...          # calendar condition (see below)
# or:
OnBootSec=...           # delay after system startup
OnUnitActiveSec=...     # delay after the last service activation
Persistent=true         # (optional: enables "catching up" at startup)
Unit=service_name       # e.g., filename.service

[Install]
WantedBy=timers.target  # so the timer activates at system startup

The timer file must have the Unit= option set in [Timer], pointing to the .service name.

We define the schedule time through OnCalendar (calendar expression) or monotonic OnBootSec, OnUnitActiveSec, etc.

As above, we can set Persistent=true so systemd executes the task after restart if the server (or PC) was off during the scheduled runtime.

3. File placement

Files are typically placed in the /etc/systemd/system/ directory (for system tasks available to all users).

They can also go into /usr/lib/systemd/system/ or at the user level (~/.config/systemd/user/).

After saving the files, it's worth checking their correctness:

systemd-analyze verify /etc/systemd/system/customname.*

If it doesn't return errors, the files are correct.

4. Starting the timer

After preparing the files, you need to load the timer into systemd and start it:

systemctl daemon-reload              # reload systemd configuration (if we added files)
systemctl start custom.timer          # starts the timer (doesn't start the service yet, but enters the schedule)
systemctl enable custom.timer         # enables the timer at system startup

As usual, with enable, an appropriate link to timers.target will be created, so the timer will be automatically activated at each system startup.

5. Complete configuration example

Let's assume a script /usr/local/bin/important.sh that we want to run 5 minutes after system startup, and then every 24 hours every morning (Mon-Fri at 10:00 AM). The unit files would look like this:

# /etc/systemd/system/important.service
[Unit]
Description=Important service

[Service]
ExecStart=/usr/local/bin/important.sh
Type=oneshot
# /etc/systemd/system/important.timer
[Unit]
Description=Timer of Important service

[Timer]
OnBootSec=5min
OnUnitActiveSec=24h
OnCalendar=Mon..Fri *-*-* 10:00:00
Unit=important.service
Persistent=true

[Install]
WantedBy=timers.target

The first will be called ~5 min after system startup, subsequent ones every 24 hours at 10:00 AM on weekdays. Thanks to Persistent=true, if the computer was turned off at the scheduled time, the timer will run the service after the system starts.


Other applications

Practically any task from crontab can be transferred to a systemd timer.

An additional possibility is user timers (in ~/.config/systemd/user/), which run tasks specific to a given account without the need for root privileges.

Debugging and monitoring timer operation

systemd provides tools for checking timer status:

Listing active timers

systemctl list-timers --all

displays a list of all timers (with "NEXT" and "LAST" columns showing the next and last invocation), you can check when the timer is next scheduled to run.

Status of a specific timer or service

systemctl status custom.timer
systemctl status custom.service

will show the current state (whether the timer is active, when the last call occurred, any errors during script execution).

System journal (journald)

All timer events and related service events are logged to journalctl. To see logs for a given timer and service:

journalctl -u custom.timer -u custom.service

You can also use the shortcut -u custom.* for both simultaneously. Useful flags:

  • -b (only current system boot)
  • -S today (from today)
  • -x (detailed messages)
  • -f (real-time tracking)

Warning

If something isn't working, the first step is always to check journalctl for error messages.

File verification

Before running, it's worth checking the syntax of the files:

systemd-analyze verify /etc/systemd/system/custom.timer
systemd-analyze verify /etc/systemd/system/custom.service

If there are errors (e.g., typos in option names), the tool will display appropriate warnings. For example, a typo like OnClendar instead of OnCalendar is usually ignored by systemd, so verify helps catch such mistakes.

systemd-analyze calendar

For testing calendar expressions (OnCalendar). E.g.

systemd-analyze calendar "Mon..Fri *-*-* 10:00"

will show the exact times of the upcoming runs in a readable form. This helps configure complex schedules correctly.


Best practices and pitfalls

Some tips for working with timers:

Full paths and environment

In .service files, always provide the full path to the script or program. The default PATH may be different than in cron. If the script uses environment variables, set them explicitly (e.g., Environment=VAR=value or in the .service file).

Type=oneshot mode

If the service performs only a one-time task (script execution), it's worth using Type=oneshot in [Service], which properly handles termination.

Unit= and filename

Make sure the .timer file has Unit=valid_custom.service precisely defined. Not linking a timer to a service is a common cause of an inactive timer.

WantedBy=timers.target

In the [Install] section, use WantedBy=timers.target instead of multi-user.target. This ensures the timer will be properly enabled when all system timers are started.

Check syntax

Even a small typo in an option name (OnCalendar, etc.) is ignored without a critical error. Always use systemd-analyze verify to catch such typos.

Monitor logs

If a timer isn't working, check the journal (journalctl) and status. You'll often find information about error causes in the logs (e.g., permissions, missing file, etc.).

Use per-user timers (if needed)

systemd allows user-level timers (run with systemctl --user). Useful for tasks that don't require root, e.g., file synchronization in Home.

Summary

systemd timers are comprehensive but require configuration discipline.

It's important to properly define .service and .timer files, use verification tools (systemd-analyze, status, journalctl), and understand which options are responsible for scheduling.