How to Create and Manage systemd Timers on Linux

A systemd timer can run commands, scripts, and routine maintenance jobs on Linux. It fills much the same role as cron, but adds built-in logging, dependency handling, support for missed runs, and randomized execution delays.

The Short Version

To create a systemd timer, write a .service unit with the command, then add a matching .timer unit with the schedule. Reload systemd and enable the timer.

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers --all

For instance, OnCalendar=*-*-* 02:30:00 schedules the associated service for 2:30 a.m. every day. If you add Persistent=true, systemd can trigger a missed calendar run once the computer starts again.

What This Guide Covers

  • How timer and service units work together
  • Scheduling a script with OnCalendar
  • Testing a calendar expression before you use it
  • Checking timer status and service logs
  • Creating timers for a regular user account

Before You Start

You’ll need a Linux distribution that runs systemd, such as Ubuntu, Debian, Fedora, Rocky Linux, or Arch Linux. Sudo access is required for system-wide units.

First, check that systemd is available:

systemctl --version

Step 1 — Write the Task Script

The example below creates a compressed backup of /etc. Keep the script separate from the unit file. That way, you can test the actual job on its own before a timer gets involved.

sudo nano /usr/local/bin/backup-etc.sh

Add this script:

#!/bin/bash
set -e
mkdir -p /var/backups/etc
/usr/bin/tar -czf /var/backups/etc/etc-$(/usr/bin/date +%F).tar.gz /etc

Make the file executable, then run it once by hand:

sudo chmod 750 /usr/local/bin/backup-etc.sh
sudo /usr/local/bin/backup-etc.sh

Before setting a schedule, confirm that the archive appears in /var/backups/etc. Better to catch a script problem now than at 2:30 in the morning.

Step 2 — Add the systemd Service

The timer usually doesn’t hold the task itself. It activates a service unit, and that service runs the command.

sudo nano /etc/systemd/system/backup-etc.service

Use the following configuration:

[Unit]
Description=Create a compressed backup of the etc directory

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-etc.sh
User=root

With Type=oneshot, systemd expects the process to carry out one job and exit. Keep paths absolute in both scripts and unit files. Services don’t inherit the same environment you get in an interactive terminal, which can make a command that worked at the prompt fail under systemd.

Step 3 — Build the Timer Unit

sudo nano /etc/systemd/system/backup-etc.timer

Put this in the file:

[Unit]
Description=Run the etc backup every day

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=5m
Unit=backup-etc.service

[Install]
WantedBy=timers.target
DirectiveWhat it does
OnCalendarSets the calendar schedule.
PersistentRuns a missed calendar task after the system becomes available again.
RandomizedDelaySecAdds a random delay so a large group of jobs doesn’t start at once.
UnitNames the service that the timer activates.

Step 4 — Check the Schedule

Before enabling anything, run systemd-analyze calendar. It checks the expression and shows the next scheduled execution time:

systemd-analyze calendar '*-*-* 02:30:00'

This is a quick way to catch a malformed expression before it reaches the timer.

Common OnCalendar Expressions

ScheduleExpression
Every day at 2:30 a.m.*-*-* 02:30:00
Every hourhourly
Monday through Friday at 6 p.m.Mon..Fri 18:00
Every 15 minutes*:0/15
First day of each month*-*-01 03:00:00

Step 5 — Start and Enable the Timer

Reload the unit definitions first. Then enable the timer and start it in the current session:

sudo systemctl daemon-reload
sudo systemctl enable --now backup-etc.timer

Enabling the timer makes it start on later boots. The --now flag handles the current session too.

Don’t enable the oneshot service. Enable the .timer unit instead. The timer starts the service when its scheduled time arrives.

Verify the Timer and Run a Test

To see active and inactive timers, along with their next run times, use:

systemctl list-timers --all

You can inspect this timer on its own as well:

systemctl status backup-etc.timer

There’s no reason to wait until 2:30 a.m. for a test. Start the service manually, then check its status:

sudo systemctl start backup-etc.service
systemctl status backup-etc.service

The systemd journal holds the service logs:

journalctl -u backup-etc.service
journalctl -u backup-etc.service --since today

After a successful oneshot service finishes, its status may read inactive (dead). That’s normal as long as the exit status shows success.

Editing or Removing the Timer

If you change either unit file, reload systemd and restart the timer so the new definition takes effect:

sudo systemctl daemon-reload
sudo systemctl restart backup-etc.timer

To stop the timer, disable it, and remove both unit files, run:

sudo systemctl disable --now backup-etc.timer
sudo rm /etc/systemd/system/backup-etc.timer
sudo rm /etc/systemd/system/backup-etc.service
sudo systemctl daemon-reload

Running a Timer Under Your User Account

A task that doesn’t need root privileges can run under your regular account. Store its units in ~/.config/systemd/user/, then include --user with the systemctl commands:

mkdir -p ~/.config/systemd/user
systemctl --user daemon-reload
systemctl --user enable --now my-task.timer
systemctl --user list-timers

User timers generally depend on that user’s systemd session. If the timer needs to run while the user is logged out, an administrator can enable lingering:

sudo loginctl enable-linger username

Mistakes That Cause Trouble

  • Using relative paths: Give commands, scripts, and output files their full paths.
  • Putting shell syntax directly in ExecStart: Pipes, redirection, and variables aren’t interpreted by a shell automatically. Put the complicated parts in a script.
  • Skipping daemon-reload: New or edited unit definitions won’t be recognized until systemd reloads them.
  • Looking only at the timer log: The timer records activation details, while task output and most failures appear under the service.
  • Treating inactive as a failure: A oneshot service normally becomes inactive after it completes.

Practical systemd Timer Habits

  • Run the command or script manually before attaching it to a timer.
  • Check calendar expressions with systemd-analyze calendar.
  • Set Persistent=true for calendar jobs that should catch up after downtime.
  • Use a randomized delay for update checks or jobs spread across a fleet.
  • Run the service as an unprivileged user if it doesn’t need root access.
  • Keep resource limits, security settings, and dependencies in the service unit rather than folding them into the schedule.

Leave a Comment

Related Posts