My cron job didn't run
What this is
A scheduled job didn't do its thing, or so it seems. Cron fails silently by design, so this page does two jobs: find out what happened, and make sure the next failure announces itself.
First: did cron fire it at all?
Check the record instead of guessing:
grep CRON /var/log/syslog | tail -20 # Debian/Ubuntu
journalctl -u cron -n 20 # or crond on RHEL-family
- The job appears at the right time → cron did its part; the command failed. Continue below.
- It doesn't appear → the schedule or the crontab itself is the problem: confirm the entry exists under the right user (
crontab -l, root's and yours are different crontabs), that the cron service runs (systemctl status cron), and that the schedule means what you think, paste the five fields into crontab.guru, which explains any expression in plain words and catches the classic day-of-week/day-of-month surprises.
The four silent killers
1. Cron's environment is nearly empty. Jobs don't get your shell's environment: PATH is typically just /usr/bin:/bin, so a command that works at your prompt (certbot, restic, node) dies with "command not found" in cron. Fix: use absolute paths (/usr/bin/certbot), or set PATH= at the top of the crontab.
2. The % character is special. In a crontab line, unescaped % means "newline, and feed the rest to stdin", so a date format like date +%F breaks the command in half. Escape every one: date +\%F.
3. Relative paths. Cron runs from the user's home directory, not your project folder, ./script.sh and relative config/output paths miss. cd /path/to/project && ./script.sh, or absolute paths throughout.
4. The output went nowhere. The job may have run and failed, and told nobody. Which is the fix that matters most:
Make failures noisy
Pick one (or both):
- MAILTO: put
MAILTO="[email protected]"at the top of the crontab and cron emails you any output, and failures produce output. (Requires the VPS to send mail; also note a job erroring every minute into a local mailbox is an inode story.) - Log it: append
>> /var/log/myjob.log 2>&1to the entry,2>&1is the crucial part, error messages go to stderr, and without it your log shows success while the job fails. For the truly critical (backups!), add a dead-man's-switch ping (healthchecks.io style) so you're alerted when the job doesn't run.
Two upgrades while you're in there
- Prevent overlaps on jobs that might outlive their interval:
flock -n /tmp/myjob.lock /path/to/command, the self-spawning pileup is one of the classic CPU killers. - Consider a systemd timer for anything important: timers log every run to journald, serialize by design, can catch up on missed runs, and don't have cron's environment quirks. Cron is fine for simple things; timers are the better tool once a job matters, the full recipe is in Using systemd timers instead of cron jobs. (
@rebootcron entries in particular are better served by a real systemd unit.)
Still need help?
You can open a support ticket. So we can help on the first reply, it's worth mentioning:
- the VPS hostname or IP,
- the exact crontab line and when you expected it to run,
- what the cron log shows around that time.
Related questions
- "Why didn't my cron job run?"
- "My script works manually but fails in cron."
- "How do I see cron logs?"
- "What does the % sign do in crontab?"
- "How do I get emailed when a cron job fails?"
- "Cron job or systemd timer, which should I use?"