devkult_
tools29converters34
home/blog/cron-job-not-running
cronlinuxdebugging

Cron Job Not Running? 10 Things to Check

The command works when you type it. You add it to the crontab, wait, and… nothing. Cron jobs fail quietly by design, so the trick is knowing where to look. Work down this list — it's ordered roughly by how often each one is the culprit.

1. Did it actually not run?

Check cron's own log before changing anything:

grep CRON /var/log/syslog          # Debian, Ubuntu
sudo journalctl -u cron --since today   # systemd (the unit is "crond" on RHEL/Fedora)

If you see your command there, cron ran it — the job itself failed. Skip to points 3–8. If you don't, start at point 2.

2. Is cron running, and did you edit the right file?

systemctl status cron    # or crond

Docker containers don't run cron unless you start it. Then make sure your entry is where you think: crontab -e edits your user's crontab, while /etc/crontab and files in /etc/cron.d/ need an extra user field before the command:

# /etc/crontab and /etc/cron.d/* — note the user column
0 3 * * * root /usr/local/bin/backup.sh

On Debian and Ubuntu, files in /etc/cron.d/ whose names contain a dot (like backup.cron) are silently ignored.

3. PATH is almost empty

Cron runs commands with a minimal PATH — typically just /usr/bin:/bin. Anything in /usr/local/bin, ~/.local/bin, nvm or pyenv directories isn't found. Use absolute paths, or set PATH at the top of the crontab:

PATH=/usr/local/bin:/usr/bin:/bin

4. Your environment isn't there either

Cron doesn't read ~/.bashrc or ~/.profile, so variables you exported in your shell don't exist. The quickest way to see the difference is to make cron dump its environment:

* * * * * env > /tmp/cron-env.txt

Compare that file with env in your terminal. Put what the job needs in a wrapper script, or define it in the crontab.

5. % means "new line"

In a crontab line, an unescaped % ends the command, and everything after it is fed to the command as input. That breaks anything using date:

# Broken: the command stops at the first %
0 0 * * * tar czf /backups/site-$(date +%F).tgz /var/www
# Works
0 0 * * * tar czf /backups/site-$(date +\%F).tgz /var/www

6. It's sh, not bash

Cron runs commands with /bin/sh, which on Debian and Ubuntu is dash. Bash-only syntax — [[ … ]], source, arrays — fails. Either set SHELL=/bin/bash at the top of the crontab, or put the logic in a script that starts with #!/bin/bash.

7. Permissions

A script must be executable by the user cron runs it as:

chmod 755 /usr/local/bin/backup.sh   # or chmod +x

(What 755 means.) Also check the files it reads and writes: a job in root's crontab creates root-owned files that your app may then be unable to open.

8. The output — and the error — went nowhere

Cron emails a job's output to the user, but most servers have no mail system, so error messages simply vanish. Redirect both streams to a log:

*/15 * * * * /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1

Nine times out of ten, that log tells you the answer in the first line.

9. Wrong time zone

Cron uses the server's time zone, and servers usually run in UTC. A job set for 0 9 * * * fires at 09:00 UTC, which might be the middle of your night. Check with timedatectl, and convert your schedule — or use CRON_TZ=Europe/London above the line if your cron supports it. Hosted schedulers such as Vercel Cron and GitHub Actions always use UTC.

10. The schedule isn't what you think

Some expressions mean something other than they appear to:

  • * 9 * * * runs every minute from 09:00 to 09:59, not once at 9 — you meant 0 9 * * *.
  • 0 0 1 * 1 runs on the 1st and on every Monday: day-of-month and day-of-week combine with OR.
  • The last line of a crontab must end with a newline, or some crons ignore it.

Paste the expression into the crontab explainer to see it in plain English with its next run times, or start from a known-good schedule in the cron library — including the awkward ones, like every 30 seconds and the last day of the month.

The fastest way to reproduce a cron failure

Run the command the way cron does — with an empty environment and sh:

env -i /bin/sh -c '/usr/local/bin/backup.sh'

If it fails there, you've reproduced the problem without waiting for the next scheduled run.

More from the blog