Cron vs systemd Timers: Scheduling Jobs on Linux in 2026
When to use cron, when to use systemd timers, how to debug a job that never ran, and using AI to translate crontab syntax and write timer units.
- #linux
- #cron
- #systemd
- #timers
- #automation
- #scheduling
Scheduled jobs are the quiet workhorses of every server, and they’re also the most under-observed thing you run. A cron job that silently fails for three weeks because nobody was watching its output is a classic Linux war story.
After 25 years of scheduled jobs, here’s how I decide between cron and systemd timers, how I debug the “it never ran” mystery, and where AI saves you from staring at five-asterisk syntax.
When cron is still the right call
cron is fine — genuinely fine — for simple, low-stakes, periodic tasks. A nightly backup, a log rotation, a cache warm. The syntax is everywhere and the tooling is universal:
crontab -e
# m h dom mon dow command
30 2 * * * /usr/local/bin/backup.sh
*/15 * * * * /usr/local/bin/poll.sh
The five fields are minute, hour, day-of-month, month, day-of-week. If you can never remember which is which — nobody can — that’s exactly what AI is for (more below).
When to reach for systemd timers instead
Timers are more setup but win whenever you need real observability or robustness:
- Logging — output goes to the journal automatically, queryable with
journalctl -u. - Missed runs —
Persistent=trueruns a job that was missed because the machine was off. cron just skips it. - Dependencies —
After=,Requires=, run only after the network or a mount is up. - Resource limits — apply
MemoryMax=,CPUQuota=,Nice=to the job. - Randomized delay —
RandomizedDelaySec=to avoid thundering-herd at the top of the hour.
For anything in production that matters, I use timers. The journal integration alone is worth it — no more redirecting cron output to a logfile you forget exists.
A systemd timer in two files
A timer is a pair: a .service that does the work and a .timer that schedules it.
/etc/systemd/system/backup.service:
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
/etc/systemd/system/backup.timer:
[Unit]
Description=Run backup nightly
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
Enable it:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
Debugging “the job never ran”
This is the #1 scheduling ticket, and the causes are predictable.
For timers, list everything and check the next run:
systemctl list-timers --all
That table shows NEXT, LAST, and the unit — instantly answering “is it even scheduled?” Then check the service’s last run:
journalctl -u backup.service --since "yesterday"
For cron, the classic failures:
- PATH is minimal. cron runs with a bare environment. Your script that works in your shell can’t find
awsornode. Fix: use absolute paths, or set PATH at the top of the script. - No output anywhere. cron emails output to the local user by default, which nobody reads. Redirect explicitly:
>> /var/log/myjob.log 2>&1. - The
%trap. An unescaped%in a crontab command is treated as a newline. Escape it:\%. - Day-of-month AND day-of-week. If you set both fields (not
*), cron runs when either matches — a surprisingly common scheduling bug.
Check that cron itself ran the job:
journalctl -u cron.service --since today # or grep /var/log/syslog for CRON
Where AI earns its place
Two jobs, both real time-savers.
Translating cron syntax. Stop guessing:
“Write a cron expression for: every weekday at 6:45am, and explain each field. Then give the equivalent systemd OnCalendar line.”
The model nails both and shows its work so you can verify. OnCalendar syntax in particular is fiddly, and AI translating from a plain-English schedule is genuinely faster than reading man systemd.time. I keep these in my Linux prompts.
Debugging a silent failure. Paste the crontab line, the script, and the journal:
“This cron job is scheduled but produces no output and the file it should create isn’t there. Here’s the crontab line, the script, and the cron journal. What’s the most likely cause? Check for PATH, environment, and output-redirection issues.”
PATH and environment differences are exactly the kind of thing AI spots immediately because it’s seen the pattern a thousand times — the same correlate-the-evidence approach I use for incident triage.
Verify the schedule, don’t trust it
One caution: AI confidently generates OnCalendar lines, and a wrong one runs at the wrong time silently. Always dry-run it:
systemd-analyze calendar "*-*-* 02:30:00"
That prints the next several trigger times so you can confirm the schedule means what you intended before you deploy it. Never ship an AI-written schedule without this check.
The takeaway
Use cron for simple periodic tasks, systemd timers for anything that needs logging, missed-run recovery, dependencies, or limits. When a job “never runs,” check list-timers or the journal first, then suspect PATH and output redirection for cron. Let AI translate schedules and debug silent failures, but verify every generated schedule with systemd-analyze calendar. A scheduled job you can’t observe isn’t automation — it’s a future war story.
AI-generated schedules are assistive. Verify trigger times with systemd-analyze calendar before deploying.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.