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 case | OnCalendar expression |
|---|---|
| Every day at 02:30 | *-*-* 02:30:00 |
| Every Monday at 06:00 | Mon *-*-* 06:00:00 |
| Hourly on workdays | Mon..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 month | Sun *-*-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:
More articles
Docker Compose vs. Podman Quadlets: SMB Perspective 2026
Docker Compose or Podman Quadlets? Comparing ecosystem, rootless operation, systemd integration and journald logging — with a migration guide for SMBs.
Windows Server 2016 End-of-Life: Migration Options for SMBs
Windows Server 2016 support ends January 2027. Migrate to Server 2025, Linux with Samba AD or Azure ESU -- options and costs compared for SMBs.
restic REST-Server as a Central Backup Target
restic REST-Server in TLS mode with append-only repositories: multi-client backups, storage planning, dedup ratios and key management for SMBs.