systemd Timers vs Cron: When to Use Which
Introduction¶
Cron still works fine for simple recurring jobs. systemd timers add things cron
never had: journal logging per run, catch-up for missed runs, randomized start
times to avoid thundering herds, and the full [Service] sandbox. Here's the
comparison and a migration path.
What timers add over cron¶
| Need | cron | systemd timer |
|---|---|---|
| See a job's output/exit status | Mail, if configured | journalctl -u job.service always |
| Run a missed job after downtime | No (anacron partly) | Persistent=true |
| Spread load (100 hosts, not all at :00) | No | RandomizedDelaySec= |
| Resource limits, sandboxing | No | MemoryMax=, Nice=, ProtectSystem=, etc. |
| Dependencies ("after the DB is up") | No | After=, Requires= |
| Run only on AC power / when idle | No | ConditionACPower=, etc. |
Cron is still fine for a trivial job on one machine where you don't need any of that, and it's one file instead of two units.
Hands-on¶
A timer is two units: foo.service (what to run) and foo.timer (when).
/etc/systemd/system/backup.service:
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
Nice=10
IOSchedulingClass=idle
/etc/systemd/system/backup.timer:
[Unit]
Description=Run backup nightly at 02:30
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
Enable the timer, not the service.
OnCalendar syntax¶
OnCalendar=hourly # top of every hour
OnCalendar=*-*-* 02:30:00 # daily 02:30
OnCalendar=Mon,Thu *-*-* 06:00:00 # Mon and Thu 06:00
OnCalendar=*-*-1 04:00:00 # first of every month
OnCalendar=*:0/15 # every 15 minutes
OnCalendar=Sat *-*-1..7 12:00:00 # first Saturday of the month
Test any expression:
systemd-analyze calendar "Mon *-*-* 06:00:00" --iterations 3
Monotonic timers (relative to boot / activation)¶
[Timer]
OnBootSec=15min # 15 min after boot
OnUnitActiveSec=1h # then every hour after each run
Verification and troubleshooting¶
systemctl list-timers --all # next run, last run, unit
systemctl status backup.timer
journalctl -u backup.service --since today
systemd-analyze verify backup.timer # syntax check
- Timer enabled but never fires — you enabled
backup.serviceinstead ofbackup.timer, or the.timerhas no[Install]section (WantedBy=timers.target). Persistent=truedidn't catch up a missed run —Persistentneeds adriftfile-style stamp in/var/lib/systemd/timers/; it works only forOnCalendar(not monotonic timers), and the machine must have been off, not the timer disabled.- Job runs but does nothing — the service runs in a clean environment: no
$PATHbeyond a minimal default, no login shell, CWD is/. Use absolute paths, setEnvironment=orEnvironmentFile=,WorkingDirectory=. - Runs as root when it shouldn't — add
User=to[Service]. - Overlapping runs (a slow job started again before finishing) — systemd
won't start a
oneshotservice that's already running for that unit, so timers naturally serialize. Cron does not. OnCalendarfires at the wrong time — timezone. Timers use the system timezone unless you addOnCalendar=... UTC. Checktimedatectl.
Converting a crontab¶
# crontab: 30 2 * * * /usr/local/bin/backup.sh
# becomes: OnCalendar=*-*-* 02:30:00
# crontab: */10 * * * * /usr/local/bin/poll.sh
# becomes: OnCalendar=*:0/10
systemd-cron (a package) auto-generates timer units from /etc/cron.* if you
want a bulk migration without rewriting each one.
Related tools and reading¶
- On-site: Crontab Generator, Linux commands reference.
- Related posts: How systemd unit files work, journalctl recipes.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Linux & Sysadmin
resolv.conf and systemd-resolved
On modern distros /etc/resolv.conf points at a stub resolver managed by systemd-resolved. Editing it directly usually does nothing — here's the real model.
October 24, 2026Tuning Open File Limits on Linux
Four different limits control how many files a process can open. 'Too many open files' means you're hitting one specific one — here's how to find and raise it.
October 24, 2026ss and conntrack for Connection Debugging
ss shows you every socket and its send/receive queues; conntrack shows what the firewall is tracking. Together they explain most 'connection refused' and 'no buffer space' problems.
October 24, 2026