systemd Timers Explained: How OnCalendar, OnBootSec, and Persistent Work

systemd timers on Linux illustrated with a scheduled task, timer activation, automated script execution, and server logs. Linux & Administration
Linux Administration / Scheduling Fundamentals

Your nightly report is scheduled for 2:30 a.m. The server is powered off at 2:30 and comes back online at 8:00. Does the job run late, wait until tomorrow, or get lost?

With systemd timers, the answer depends on a few settings you can read in a small configuration file. This guide explains which clock each timer uses, what happens after downtime, and how to check a schedule before trusting it on a server.

Beginner-friendlyRead-only commandsSystemd-based Linux
Quick answer: how systemd timers schedule work

OnCalendar= schedules by the clock on the wall. OnBootSec= schedules relative to boot time. OnUnitActiveSec= schedules relative to when the associated unit was last activated. OnUnitInactiveSec= measures from when it finished and deactivated. Persistent=true can trigger one catch-up activation for a missed OnCalendar= event when the timer becomes active again; it does not replay every missed occurrence.

1. How systemd timers start services

The easiest way to understand systemd timers is to separate when from what. A .timer unit holds the timing rules; the associated .service unit runs the command. By default, daily-report.timer activates daily-report.service. A timer can target a different unit using Unit=.

This separation is useful when a job fails: you can check whether its schedule elapsed, then inspect the service for the actual execution result. If you’re new to systemd units, the systemd units and boot process guide covers the bigger picture.

A small but important detail

If the service is already active when the timer elapses, systemd does not start another copy of that unit. It also does not create a queue of missed copies. This differs from assuming every clock tick launches a separate process. A recurring Type=oneshot service also generally should not use RemainAfterExit=yes, because that would leave the service active after the command finishes.

2. Which clock do systemd timers use?

Not all systemd timers mean “run at 2:30.” Some schedules follow civil time; others follow elapsed time since a particular event.

OnCalendar=

Matches a calendar expression such as every day at 02:30. This follows the system’s wall clock and configured timezone.

OnBootSec=

Counts from the machine’s boot time, such as five minutes after startup. This is not an everyday calendar event.

OnUnitActiveSec=

Counts from the last activation of the target unit. Useful for repeated work tied to previous activations.

GoalSettingHow to read it
Report every morning at 02:30OnCalendar=*-*-* 02:30:00Use calendar time; check timezone and clock synchronization.
Run once five minutes after startupOnBootSec=5minMeasured relative to boot. If the timer is activated after that point has already passed, it can elapse immediately.
Repeat ten minutes after each activationOnUnitActiveSec=10minMeasured from activation, not ten minutes after the job finishes.
Wait ten minutes after each completed runOnUnitInactiveSec=10minMeasured from when the activated unit becomes inactive; provide a separate initial trigger or start the service first.

Systemd timers can also combine multiple timing directives: whichever condition becomes due can trigger the associated unit. For instance, OnBootSec=5min plus OnUnitActiveSec=1h is a common shape for periodic work that begins after startup. Source: systemd.timer(5).

Boot time is not login time

In a user systemd manager, OnStartupSec= is often the more relevant reference point: it counts from when that user manager started. Do not assume a user timer and a system timer share identical boot-time behavior.

3. Read this timer before copying anything

Consider a hypothetical nightly status report scheduled with systemd timers on one Linux server. It should run around 02:30 every day, and if the machine was switched off overnight, one late run after startup is acceptable. These are example unit files for understanding the relationship, not a record of a tested deployment.

Example / daily-report.service
[Unit]
Description=Example daily status report

[Service]
Type=oneshot
ExecStart=/usr/bin/uptime
Example / daily-report.timer
[Unit]
Description=Schedule the example daily report

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
AccuracySec=1min
Unit=daily-report.service

[Install]
WantedBy=timers.target

Type=oneshot tells systemd to run a finite task rather than keep a daemon alive. The command here only reports uptime; an actual deployment should use a deliberate script, account, permissions, and logging policy. See how to write a systemd service unit for that groundwork.

Why include AccuracySec=1min?

Systemd timers are not guaranteed to dispatch at a microsecond-precise instant. The default accuracy window is one minute; this line makes the intended tolerance visible. For wider scheduling windows or reduced load spikes, systemd also offers RandomizedDelaySec=, which adds a random delay. It solves a different problem from AccuracySec=.

4. What happens when systemd timers miss a run?

systemd timers illustrated by a missed overnight calendar slot and a morning catch-up schedule
Illustration: a scheduled event can pass while a server is offline. With OnCalendar= and Persistent=true, a late activation may be requested when the timer becomes active again; individual missed events are not queued.

For systemd timers, the missed-run behavior is often the deciding detail. Imagine the report is due at 02:30, but the server is offline from midnight to 08:00. Compare the conditions below.

Case A / Calendar + Persistent

One late activation

With OnCalendar= and Persistent=true, activating the timer again can request a catch-up start because at least one scheduled event passed while it was inactive.

Case B / Calendar without Persistent

Wait for the next match

Without persistence, the timer ordinarily waits for its next scheduled calendar match after activation. Yesterday’s slot is not treated as pending work.

Case C / Boot-relative timer

A different rule entirely

OnBootSec=5min has no “yesterday’s 02:30” to recover. It becomes due relative to boot, and a past boot-relative deadline may elapse immediately when activated.

Crucial limit: even if the server missed three daily calendar events, Persistent=true is not an instruction to execute the task three times. It allows a catch-up activation, not a durable work queue. And a catch-up is still affected by applicable scheduling delays and whether the associated unit is already active. Source: systemd.timer(5), Persistent=.

If every missed job matters, a timer is not enough.

For billing, payment reconciliation, or other work that must process each date exactly once, let the application track completed periods in durable storage. A systemd timer decides when to try to run a service; it does not prove business-level completion or replay each missed transaction.

5. Two read-only checks for systemd timers

You can inspect systemd timers without adding files to /etc/systemd/system, enabling anything, or modifying existing services. These commands are read-only; they require systemd tooling on your Linux machine.

Check the next three calendar matches

Terminal / parse a schedule, no changes made
systemd-analyze calendar --iterations=3 '*-*-* 02:30:00'

Look for the normalized expression and its next three matching dates. The exact dates and timezone depend on your machine and when you run the command. If the expression is invalid, the parser reports an error rather than installing a bad schedule. This tests the calendar expression—not whether a service will succeed.

Editorial syntax check

For this guide, we ran the command above in an isolated Debian 13 environment using systemd 257.9. The parser accepted the expression, normalized it to *-*-* 02:30:00, and produced three future daily matches. No production timer or service execution was tested.

See which timers your system knows about

Terminal / inspect timers without changing them
systemctl list-timers --all

Read the NEXT, LAST, UNIT, and ACTIVATES columns. Unit names and timestamps vary by distribution. An absent or blank NEXT value is a reason to inspect that timer’s state, not automatic proof that the underlying application failed. For command details, see systemd-analyze(1) and systemctl(1).

Why these checks are worth doing

A calendar rule describes when the timer becomes due. A service’s exit status and logs tell you what the work actually did. Once you create a test timer in Lesson 02, inspect both sides rather than relying on one “active” label. For log-reading basics, see journalctl Explained.

6. Five essential rules for safe scheduling

Use these five rules when choosing settings for systemd timers. Each one prevents a different scheduling assumption from turning into a surprise.

  • Fixed wall-clock time: start with OnCalendar=. Verify how your timezone and daylight-saving changes affect that time.
  • Once shortly after startup: consider OnBootSec=. Understand what happens if the timer activates after the chosen interval.
  • Repeat relative to the previous start: consider OnUnitActiveSec=. For a gap after completion, compare OnUnitInactiveSec=.
  • One catch-up after a missed calendar slot: add Persistent=true, but do not assume it queues all missed work.
  • Precise dispatch or distributed load: review AccuracySec= and RandomizedDelaySec= deliberately instead of accepting timing assumptions.

If your job already runs well with cron, you do not need to replace it merely because timers exist. The separate Cron vs systemd Timers comparison helps with that choice. If you’re still learning cron syntax, start with Linux Cron Jobs Explained.

Quick knowledge check

A service starts at 10:00 and finishes at 10:07. What does OnUnitActiveSec=10min measure from?

From the unit’s last activation at 10:00, not from its finish at 10:07. Compare OnUnitInactiveSec= for an interval measured from deactivation.

The machine misses five daily slots. Does Persistent=true launch five jobs on reboot?

No. It can request a catch-up activation for a missed calendar event, but it is not a queue containing five executions.

What should you check after systemd-analyze confirms the calendar expression?

Check the timer’s activation status and the associated service’s actual result and logs. A valid schedule alone does not guarantee successful work.

The decision to take away

When configuring systemd timers, choose the reference clock first. Decide whether one late run is useful after downtime. Then keep the schedule and the work separate enough that you can inspect both. In Lesson 02, we’ll turn this model into a working timer and a safe test script.

Prepared by Servers Academy Editorial Team. This lesson contains no affiliate recommendations or sponsored product placements. The example units and scenarios are hypothetical; the separately noted calendar-parser check is a real syntax check, not a production benchmark. Publication and substantive-update dates come from the WordPress post metadata.

Technical references

  1. systemd.timer(5) — timer directives, elapsed-time rules, persistence, and accuracy.
  2. systemd.time(7) — calendar and time-span expression syntax.
  3. systemd-analyze(1) — validation of calendar schedules.
  4. systemd.service(5) — one-shot service behavior.
  5. systemd upstream source documentation — latest directive definitions; use your installed man systemd.timer for version-specific behavior.

Version note: systemd options differ across releases. Check systemd --version and the manual pages installed on your server before relying on settings that may be unavailable in an older distribution. For service deployment and hardening, follow your distribution’s security guidance.

Rate article
Servers Academy
Add a comment