Skip to main content
Comparisons

Cron vs systemd Timers: Which Should You Use for Scheduled Jobs?

By Byteary Team · Sep 2, 2026 · 3 min read

Cron vs systemd Timers: Which Should You Use for Scheduled Jobs?

On a modern Linux server you have two built-in ways to run a job on a schedule: the classic cron daemon, and systemd timers. Both will run your backup script at 02:30. They differ in what happens when things go wrong - a missed run, a job that hangs, a failure nobody noticed. Here is the same job written both ways, then a straight comparison.

The same job, two ways

With cron, it is one line in crontab -e:

30 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

With systemd, it is two small files. A service that says what to run - /etc/systemd/system/backup.service:

[Unit]
Description=Nightly database backup

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

And a timer that says when - /etc/systemd/system/backup.timer:

[Unit]
Description=Run the backup every night

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=5min

[Install]
WantedBy=timers.target

Then sudo systemctl daemon-reload and sudo systemctl enable --now backup.timer. The systemd Service Generator writes the unit file; set Type=oneshot and drop the restart policy for a scheduled job. For the cron line, use the Crontab Generator.

Byteary systemd Service Generator output showing a unit file with Unit, Service and Install sections for a nightly database backup
Generate the service unit, then add a matching .timer file to schedule it.

Side-by-side comparison

cronsystemd timers
SetupOne lineTwo unit files
Schedule syntax30 2 * * *OnCalendar=*-*-* 02:30:00, or relative times like OnBootSec=
Missed runs (server was off)SkippedRun at next boot with Persistent=true
LogsWhatever you redirect; output may be mailedCaptured in the journal: journalctl -u backup
Overlapping runsPossible; add flock yourselfA service cannot start twice while running
Resource limitsNone built inCPUQuota=, MemoryMax=, Nice=
DependenciesNoneAfter=, Requires= (e.g. wait for the network or a mount)
See next runNot built insystemctl list-timers
PortabilityEvery Unix, most containersLinux with systemd only

Where cron wins

  • Simplicity. One line, readable by anyone, easy to review in a pull request.
  • Portability. The same syntax works on macOS, BSD, older servers, shared hosting control panels, Kubernetes CronJobs and GitHub Actions - see our cron expressions guide.
  • Per-user jobs without root access, via crontab -e.

Where systemd timers win

  • Reliability. Persistent=true catches up on runs missed while the machine was down - important for laptops and servers that reboot for updates.
  • Visibility. Every run's output and exit status is in the journal, and systemctl status backup shows whether the last run failed.
  • Safety. No overlapping runs, resource limits, a dedicated user, and sandboxing options such as ProtectSystem=.
  • Testing. systemctl start backup.service runs the job now, in exactly the same environment as the scheduled run, and systemd-analyze calendar "Mon..Fri 09:30" shows when an OnCalendar expression next fires.

The systemd.timer manual documents every option.

Verdict

For a quick, low-stakes job - clearing a cache folder, pinging a URL - cron is perfectly fine and easier to read. For anything you would be upset to find silently failing - backups, billing, certificate renewals - use a systemd timer: the missed-run handling, journal logging and overlap protection are worth the extra file.

If you stay with cron

Close the gaps by hand: redirect output to a log file (as in the example), wrap long jobs in flock -n /tmp/backup.lock so they cannot overlap, and set MAILTO= at the top of the crontab so failures reach someone. Test the expression with the Cron Expression Builder - it shows the next run times - before you rely on it.

Comments (0)

Leave a Comment

CAPTCHA image - enter the characters shown

Your comment will appear after it's been reviewed.

Related Posts