Remote Support Start download

Linux systemd Timers: A Better Cron

LinuxsystemdAutomation
Linux systemd Timers: A Better Cron

Cron has been a fixed part of every Unix system since the 1970s. But anyone administering modern Linux servers today — whether Debian 13, Ubuntu 26.04 LTS or Rocky Linux 10 — has a much more powerful tool at hand with systemd. systemd timer units replace classic cron in many scenarios and bring features that save considerable time and nerves in production environments.

In this article, we show why we at DATAZONE consistently equip new servers with timer units, what advantages this brings, and what a practical backup timer looks like in real life.

Where cron reaches its limits in modern environments

Cron does its job reliably — as long as the system is running, the cron daemon is active and the job is clearly defined. In practice, however, questions arise regularly that cron cannot or can only awkwardly answer:

  • Was the nightly backup job actually executed even though the server was off at the scheduled time?
  • Why does the script break even though it runs manually? (Answer: a different environment, no PATH, no TTY.)
  • How do I prevent 200 servers from pulling the same repository at exactly the same second?
  • Where do I find structured logs of the last run, including exit code and duration?

Cron delivers little here. Output ends up as e-mail to root, is rarely configured, and a job that was not executed usually leaves no trace. systemd timers address precisely these weaknesses.

Structure of a timer unit: service plus timer

Each systemd timer consists of two files: a service unit defining what is executed, and a timer unit defining when. Both are typically placed under /etc/systemd/system/.

Example: a daily backup job that rsyncs a directory to /srv/backup/.

# /etc/systemd/system/datazone-backup.service
[Unit]
Description=DATAZONE Daily Backup
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/datazone-backup.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7

And the matching timer:

# /etc/systemd/system/datazone-backup.timer
[Unit]
Description=Run DATAZONE Backup daily

[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=30min
Persistent=true
Unit=datazone-backup.service

[Install]
WantedBy=timers.target

The timer is activated with two commands:

systemctl daemon-reload
systemctl enable --now datazone-backup.timer

OnCalendar syntax: more expressive than cron

The OnCalendar= directive uses its own, readable syntax. It covers all cron cases and additionally allows constructs that are cumbersome or even impossible in cron.

Use caseOnCalendar expression
Every day at 02:30*-*-* 02:30:00
Every Monday at 06:00Mon *-*-* 06:00:00
Hourly on workdaysMon..Fri *-*-* *:00:00
First day of each month*-*-01 03:00:00
Every 15 minutes*-*-* *:00/15:00
Quarterly*-01,04,07,10-01 04:00:00
Last Sunday of the monthSun *-*-22..28 05:00:00

Very useful is systemd-analyze calendar for validating an expression before deployment:

systemd-analyze calendar "Mon..Fri *-*-* 02:30:00"

The output shows the next actual execution time — a detail that often has to be estimated mentally with cron entries.

Four advantages that count in practice

1. Persistent=true: catch-up execution

With Persistent=true, systemd remembers the last execution time under /var/lib/systemd/timers/. If the server was off at the actual time, the job is caught up after boot. For laptops, virtual machines with schedules or servers at edge locations, this is a decisive advantage over cron — there, the moment would simply be missed.

2. RandomizedDelaySec: no load spikes

A classic in the data center: 50 VMs start their backup job at 02:00, hit the storage pool simultaneously and cause massive I/O congestion. With RandomizedDelaySec=30min, systemd distributes the actual start time randomly across the specified interval. The load spreads, the storage breathes again.

3. Journal logging: structured and searchable

Every timer run lands automatically in the systemd-journal. Instead of manually rotating logfiles, you query the data specifically:

journalctl -u datazone-backup.service --since "yesterday"
journalctl -u datazone-backup.service -p err
systemctl status datazone-backup.timer

You see exit code, runtime, stdout, stderr and trigger cause in a consistent format. For central monitoring, the journal can be forwarded to Loki, Graylog or Splunk.

4. Dependencies and resource control

A service unit knows After=, Requires=, Wants=, Conflicts=. A backup job can thus wait explicitly for network-online.target or a mount. Via Nice=, IOSchedulingClass=, MemoryMax= or CPUQuota=, you control resources precisely — everything that with cron would only work via wrapper scripts.

list-timers: all schedules at a glance

Perhaps the most-used command in daily life is systemctl list-timers. It shows all active timers, the next and last execution time, and the associated unit.

NEXT                        LEFT          LAST                        PASSED       UNIT                          ACTIVATES
Thu 2026-06-04 02:30:00 CEST 8h            Wed 2026-06-03 02:31:14 CEST 15h ago      datazone-backup.timer         datazone-backup.service
Thu 2026-06-04 03:10:00 CEST 9h            Wed 2026-06-03 03:10:00 CEST 14h ago      logrotate.timer               logrotate.service
Thu 2026-06-04 06:00:00 CEST 12h           Wed 2026-06-03 06:00:12 CEST 11h ago      apt-daily-upgrade.timer       apt-daily-upgrade.service
Thu 2026-06-04 18:42:31 CEST 1day 1h       Wed 2026-06-03 18:42:31 CEST 23h ago      fstrim.timer                  fstrim.service
Sun 2026-06-08 00:00:00 CEST 4days         Sun 2026-06-01 00:01:55 CEST 2days ago    snapper-cleanup.timer         snapper-cleanup.service

Sorted by next execution, you immediately see what runs when. With --all, inactive timers are included too. This overview saves a lot of time during handovers and audits — especially in environments where cron entries that have grown over years are often no longer fully known to anyone.

When cron is still the right choice

Despite all advantages, there are cases where classic cron remains sensible: individual ad-hoc jobs of a user via crontab -e, very old distributions without full systemd integration, or containers that do not run systemd. For pure user tasks, crontab is often quicker to write too. But for anything that belongs to server configuration, the timer is the more robust choice.

DATAZONE: automation set up properly

Whoever operates a Linux stack — whether as a hypervisor under Proxmox, as a NAS with TrueNAS, or as a backup target via Proxmox Backup Server — benefits from consistently documented timer units instead of scattered cron entries. We set up automation cleanly right at onboarding: timer units, journal logging, monitoring integration, restore tests.

DATAZONE supports you with Linux automation, building your own timer units, and cleaning up historically grown cron landscapes. We are based in Neuburg an der Donau, serve customers throughout the DACH region, and advise vendor-independently. Reach out via our Linux consulting or directly via the contact form.

More on these topics:

Need IT consulting?

Contact us for a no-obligation consultation on Proxmox, OPNsense, TrueNAS and more.

Get in touch