Why is my cron job not running
Tools Nimbus is a free, no-signup developer toolkit that runs entirely in your browser, so your data is never uploaded to a server. A cron job that does not run usually has one of four causes: the schedule expression does not mean what you think, the environment lacks the PATH your shell gives you, the output including the error is being thrown away, or the crontab file has no trailing newline. Check what your schedule actually means with the Tools Nimbus Cron Expression Explainer before changing anything else.
Last updated August 2026
First, find out whether it ran
"Not running" covers two very different faults, and they have different fixes: cron never started the job, or cron started it and the job failed. Separate them before you debug anything.
On most Linux systems, cron logs each invocation. Try grep CRON /var/log/syslog on Debian and Ubuntu, or journalctl -u cron (or -u crond) on systemd hosts. If you see your command listed at the expected time, cron did its part and the problem is inside your script. If there is no entry at all, the schedule or the crontab itself is at fault.
The quickest way to settle it is a job that cannot fail. Add * * * * * /bin/date >> /tmp/cron-test.log 2>&1, wait two minutes, and look at the file. If it fills, cron is healthy and the problem is specific to your job.
Cause 1: the environment is not your shell
This is the single most common reason a script that works at the prompt does nothing under cron. An interactive login sources .bashrc, .profile, and whatever a version manager such as nvm, pyenv or rbenv installs. Cron sources none of that. It gives you a minimal environment, typically PATH of just /usr/bin:/bin, and often no HOME beyond the bare default.
The consequences are predictable once you know to look for them:
node,python3,awsordockerare not found, because they live in/usr/local/binor a version manager's shim directory.- Relative paths resolve against the wrong directory. Cron starts you in the user's home, not the script's location.
- Secrets exported in your shell profile are absent, so the script authenticates as nobody.
The fixes, in order of preference:
# 1. Absolute paths to everything
0 3 * * * /usr/local/bin/node /srv/app/scripts/nightly.js
# 2. Or set PATH at the top of the crontab
PATH=/usr/local/bin:/usr/bin:/bin
# 3. Or have the entry load a login shell
0 3 * * * /bin/bash -lc '/srv/app/scripts/nightly.sh'Option three is convenient but it makes the job depend on your dotfiles, which is a fragile thing for a scheduled task. Absolute paths are duller and far more reliable.
Cause 2: the schedule does not mean what you think
Standard cron takes five fields: minute, hour, day of month, month, day of week. Several conventions trip people up.
| Expression | What people expect | What it means |
|---|---|---|
* * * * * | Every second | Every minute. Cron has no seconds field. |
0 0 13 * 5 | Friday the 13th | The 13th, OR any Friday |
*/30 * * * * | Every 30 minutes from now | At minute 0 and minute 30 of each hour |
0 9 * * 7 | Invalid | Sunday. Both 0 and 7 mean Sunday. |
0 0 31 * * | End of every month | Only months with 31 days, so 7 times a year |
The OR behaviour of the two day fields is genuinely surprising and it is in the specification. If you need Friday the 13th, restrict the day of month in cron and check the weekday inside the script.
Paste your expression into the Cron Expression Explainer to see it described in plain English before you commit it. If you are new to the syntax, what is a cron job covers the fields from the beginning.
Cause 3: the error is being discarded
Cron mails a job's output to the owning user. On a typical cloud server there is no mail transport configured, so that output goes nowhere at all. The job fails, the reason is written to a mailbox that does not exist, and from the outside it looks identical to a job that never ran.
Always capture output. Both streams, to a real file:
0 3 * * * /srv/app/run.sh >> /var/log/myjob.log 2>&1The 2>&1 matters, and it has to come after the redirect. Without it you keep stdout and lose stderr, which is exactly the half you need. A job redirected to /dev/null is undebuggable by construction, and it is depressingly common in real crontabs.
Cause 4: the crontab file itself
- No trailing newline. A final line without one can be skipped silently. Always leave a blank line at the end.
- Unescaped percent signs. In a crontab,
%means a newline and everything after the first one becomes stdin for the command. A date format such asdate +%Y-%m-%dmust be writtendate +\%Y-\%m-\%d. - The wrong user's crontab.
crontab -eedits yours;sudo crontab -eedits root's. A job placed in one and looked for in the other appears to have vanished. - System crontab needs a user field. Entries in
/etc/crontaband/etc/cron.d/take a six-field form with the user before the command. Using the five-field form there breaks the entry. - Permissions. The script must be executable, and if you invoke it directly it needs a correct shebang line.
Cause 5: timezone
Cron uses the system timezone. Servers are commonly UTC, so a job you set for 09:00 fires at 10:00 local time during British Summer Time, and the offset changes twice a year. Check with date on the host rather than assuming. Some cron implementations accept a CRON_TZ=Europe/London line at the top of the crontab; where it is unsupported, schedule in UTC deliberately and write the local intention in a comment.
The other timezone trap is the clock changing underneath a job. Jobs scheduled in the hour that is skipped in spring may not run at all, and jobs in the hour repeated in autumn may run twice. Anything that must happen exactly once needs to be idempotent.
A checklist
- Confirm from the cron log whether it started at all.
- Redirect stdout and stderr to a file, then wait for the next run.
- Use absolute paths for every binary and every file.
- Verify the expression in the Cron Expression Explainer.
- Check the host timezone with
date. - Escape any
%, and leave a trailing newline. - Make sure you are looking at the right user's crontab.
For more developer fixes, see what is a cron job for the syntax itself, or browse the Tools Nimbus guides.