Linux

Cron and systemd Timers

·2 min read

Cron is older than most of the people writing modern web services. It is also still, in 2026, the right answer for most scheduled tasks. systemd timers are a strict superset of cron's capabilities; whether you should switch depends on what you're scheduling.

Crontab syntax

A crontab line has five time fields and a command:

# m h dom mon dow command
0  3 *   *   *   /usr/local/bin/backup.sh
*/5 *  *   *   *   /usr/local/bin/heartbeat.sh
0  9 *   *   1-5 /usr/local/bin/weekday-report.sh
0  0 1   *   *   /usr/local/bin/monthly.sh
@reboot          /usr/local/bin/on-boot.sh

The shortcuts (@hourly, @daily, @weekly, @monthly, @reboot) save you remembering the field order.

User vs system crontabs

Two parallel systems: user crontabs live in /var/spool/cron/ and are edited with crontab -e. System crontabs live in /etc/crontab and /etc/cron.d/; they have an extra user field between the time spec and the command. Directories like /etc/cron.daily/ are run by an /etc/crontab entry.

The environment trap

Cron runs jobs with a minimal environment. Your $PATH is something like /usr/bin:/bin, your HOME is set, almost nothing else. Scripts that work interactively but fail under cron almost always fail because of $PATH.

# At the top of cron jobs:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Or in the crontab itself (variables apply to all jobs below):
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 3 * * * /opt/myapp/backup.sh

Anacron

Cron only runs jobs if the machine is up at the scheduled time. Anacron catches up — if the laptop was asleep when the daily job should have fired, anacron runs it on next boot. Most desktop distributions ship anacron handling for /etc/cron.daily/ and friends.

When to use systemd timers

systemd timers are more verbose but offer things cron can't: dependency on other units, randomized delays to avoid thundering herd, calendar expressions with seconds and timezones, accurate journald logging, and resource isolation through the underlying service unit.

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
Nice=10

# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup nightly

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=30m
Persistent=true

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers

Choose cron when

  • The task is simple and the schedule is "at 3 AM"
  • You don't need failure alerting beyond email
  • You want one-line readability

Choose systemd timers when

  • The job depends on the network being up, or another unit being active
  • You need Persistent=true (catch up after downtime)
  • You want logs in journald instead of mail
  • You're already managing the workload as a systemd service

Related