Cron job not running / crontab not executing
You put a line in crontab, waited, and nothing happened. Most of the time the schedule is fine — the environment around it is not.
Decode the expression before blaming cron
A surprising number of "not running" reports are schedules that never meant what the author thought. Paste the five fields (or an @hourly-style shortcut) into the Cron Expression Explainer and read the plain-English sentence plus the next run times in local and UTC. From a script or CI, the same breakdown is a keyless JSON call:
curl -sG 'https://api.oddlathe.com/v1/cron/explain' \
--data-urlencode 'expr=0 9 * * 1-5' \
--data-urlencode 'tz=Europe/London' \
--data-urlencode 'n=5'
See also cron every 5 minutes and @reboot.
Checklist when nothing fires
| Check | Command / place | Typical miss |
|---|---|---|
| Right crontab | crontab -l as the intended user; sudo crontab -u deploy -l; /etc/cron.d/ files | Edited root's crontab while the app runs as www-data |
| Daemon up | systemctl status cron (Debian/Ubuntu) or crond (RHEL) | Minimal container image with no cron installed |
| Time zone | timedatectl; cat /etc/timezone; optional CRON_TZ=Europe/London line above the job | "9am" meant London but the VPS is UTC |
| PATH / cwd | Cron's default PATH is tiny (/usr/bin:/bin); no login profile | node, python, npm "not found" — job exits in milliseconds |
| Absolute paths | Use /usr/bin/node /home/app/run.js or set PATH= on the line | Relative ./script.sh from an unknown cwd |
| Day OR rule | Both day-of-month and day-of-week restricted | 0 0 13 * 5 runs every 13th and every Friday, not only Friday the 13th |
| Output / mail | MAILTO=, redirect >>/var/log/job.log 2>&1 | Job ran, failed, and the only clue was discarded mail |
| Permissions | Script executable; readable by that user; SELinux/AppArmor rare but real | Permission denied running script |
| Locked / nologin | passwd -S user; shell in /etc/passwd | Some systems skip cron for locked or nologin accounts |
Where the logs are
- Debian/Ubuntu:
grep CRON /var/log/syslogorjournalctl -u cron -n 100 --no-pager - RHEL/CentOS/Fedora:
grep CROND /var/log/cronorjournalctl -u crond - macOS: modern macOS prefers
launchd; legacy cron is limited — check Console.app forcron - Your redirect: always better than hoping mail works —
… >>/home/app/logs/job.log 2>&1
Environment traps
# Bad — relies on interactive PATH
*/5 * * * * node /home/app/job.js
# Better — absolute binary + log
*/5 * * * * /usr/bin/node /home/app/job.js >>/home/app/logs/job.log 2>&1
# Or set PATH on the line / in the crontab header
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
CRON_TZ=Europe/London
0 9 * * 1-5 /home/app/bin/weekday.sh
Cron does not load ~/.bashrc, nvm, pyenv or systemd user environment. If you need those, call a wrapper script that sources them explicitly, or use a systemd timer with Environment= / EnvironmentFile=.
Docker, Kubernetes and CI
- Docker: the process you
CMDis usually the app, not cron. Either run cron as PID 1 with a real crontab, use an external scheduler, or a sidecar. A stopped container never fires "cron". - Kubernetes: prefer a
CronJobresource over a crontab inside a Pod. Checkkubectl get cronjobsand recent Jobs forBackoffLimitfailures. - GitHub Actions / CI "cron": schedule is UTC only; repos that are inactive can skip scheduled workflows — that is not server cron.
Still stuck?
- Run the exact command from the crontab by hand as that user:
sudo -u deploy -H /usr/bin/node /home/app/job.js - Temporarily set
* * * * *(every minute) with adate >>/tmp/cron-proof.txtline to prove the daemon is alive, then put the real schedule back. - Confirm next fire times with the explainer in the server's time zone (often UTC on cloud VMs).
- Mystery paste from a ticket? What is this?
Related
Cron every 5 minutes · cron @reboot · Cron Expression Explainer · Anvil cron explainer API · Permission denied running script · EADDRINUSE
Questions
Why is my cron job not running?
Usually the wrong user's crontab, a PATH that cannot find your binary, a time-zone mismatch (UTC vs local), or an expression that does not mean what you think. Decode it first, then check syslog/journalctl.
How do I see if cron tried to run my job?
On Debian/Ubuntu: grep CRON /var/log/syslog or journalctl -u cron. On RHEL: /var/log/cron. Also redirect your own stdout/stderr to a log file.
Does cron use my .bashrc?
No. Cron has a minimal environment. Use absolute paths, set PATH/SHELL/CRON_TZ in the crontab, or call a wrapper script that sources what you need.
Why did 0 0 13 * 5 run on a Thursday?
When both day-of-month and day-of-week are restricted, classic cron uses OR: it runs on the 13th and on Fridays. The explainer warns about that.
Cron vs systemd timers?
Timers integrate with journald, dependencies and EnvironmentFile. Cron is still fine for simple user jobs; pick one and log deliberately.
How can I get next run times from a script?
GET https://api.oddlathe.com/v1/cron/explain?expr=…&tz=…&n=5 (docs at /docs/cron-explain). No API key.
Published .