Tools Nimbus

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, aws or docker are not found, because they live in /usr/local/bin or 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.

ExpressionWhat people expectWhat it means
* * * * *Every secondEvery minute. Cron has no seconds field.
0 0 13 * 5Friday the 13thThe 13th, OR any Friday
*/30 * * * *Every 30 minutes from nowAt minute 0 and minute 30 of each hour
0 9 * * 7InvalidSunday. Both 0 and 7 mean Sunday.
0 0 31 * *End of every monthOnly 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>&1

The 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 as date +%Y-%m-%d must be written date +\%Y-\%m-\%d.
  • The wrong user's crontab. crontab -e edits 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/crontab and /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

  1. Confirm from the cron log whether it started at all.
  2. Redirect stdout and stderr to a file, then wait for the next run.
  3. Use absolute paths for every binary and every file.
  4. Verify the expression in the Cron Expression Explainer.
  5. Check the host timezone with date.
  6. Escape any %, and leave a trailing newline.
  7. 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.

Frequently asked questions

Why does my script work manually but not from cron?+

Cron runs with a nearly empty environment. Your interactive shell loads .bashrc, .profile and any version manager, so PATH contains everything you expect; cron typically has only /usr/bin and /bin, and no PYENV, NVM or similar. A command that resolves fine at the prompt fails with command not found under cron. Use absolute paths to every binary, or set PATH explicitly at the top of the crontab.

Why does my crontab entry never fire at all?+

The most common cause is a missing newline at the end of the file. A crontab whose final line has no trailing newline can be ignored entirely by the daemon, silently. Check the schedule expression too: a five-field line is minute, hour, day of month, month, day of week, and putting seconds in the first field shifts everything along by one.

Why does my cron job run at the wrong time?+

Cron uses the system timezone, not yours, and on a server that is very often UTC. A job set for 09:00 runs at 09:00 UTC, which is 10:00 in British Summer Time. Check with the date command on the host, and set the CRON_TZ variable at the top of the crontab if your cron implementation supports it.

Why do the day-of-month and day-of-week fields behave oddly together?+

When both are restricted, cron treats them as OR rather than AND. An entry of 0 0 13 * 5 does not mean Friday the 13th; it means the 13th of every month AND every Friday. To get a genuine AND you have to restrict one field in cron and test the other inside your script.

Where do cron errors go?+

By default cron emails the job's output to the owning user, and on most servers there is no mail transport, so it vanishes. That is why a failing job looks like a job that never ran. Redirect both streams to a file, for example appending >> /tmp/job.log 2>&1 to the entry, and you will have the error the next time it fails.

Try these browser-based tools mentioned in this guide. Everything runs locally, so your data never leaves your device.