Why Your Cron Job Is Not Running (And How to Fix It)

Conceptual illustration of cron job troubleshooting, with broken automation on one side and a successful scheduled workflow on the other, highlighting logs, syntax, permissions, and environment issues.

If your cron job is not running, the cause is almost always one of five things: cron uses a stripped-down environment (so your PATH and variables are missing), your schedule syntax is off by a field, the script isn't executable or has the wrong shebang, the crontab was never actually loaded, or the output errors are being silently discarded. The fastest way to find the real reason is to redirect your job's output to a log file and read what cron actually did.

Confirm cron is actually running

Before blaming your script, check that the cron daemon itself is alive. On most Linux systems the service is called cron or crond .

# Debian/Ubuntu
systemctl status cron

# RHEL/CentOS/Fedora
systemctl status crond

# If it's not running:
sudo systemctl start cron
sudo systemctl enable cron

Next, confirm your crontab was actually saved. A surprising number of "cron not executing" reports come from editing a file that cron never reads. List the crontab for the current user:

crontab -l

If your job isn't there, you either edited the wrong file or forgot to save. Always use crontab -e to edit, never edit files under /var/spool/cron by hand.

Root vs. user crontabs are separate. A job in your personal crontab won't run as root, and vice versa. Check sudo crontab -l and your own crontab -l to see which one holds the job.

Capture the output so you can see the error

Cron runs silently. When a job fails, you get nothing on screen because there's no attached terminal. By default cron emails output to the user, but most servers have no mail configured, so errors vanish. Force the output somewhere you can read it:

* * * * * /home/user/backup.sh >> /tmp/cron.log 2>&1

The 2>&1 part is the key piece. It sends error messages (stderr) into the same log as normal output (stdout). Without it you'd only capture successful chatter and miss the actual failure.

You can also watch the system log to confirm cron even attempted your job:

# Debian/Ubuntu
grep CRON /var/log/syslog

# RHEL/CentOS
grep CRON /var/log/cron

# systemd systems
journalctl -u cron --since "10 minutes ago"

If you see your command listed in the log but it still didn't produce results, the problem is inside the script or its environment. If you see nothing at all, cron never triggered it, which points to a syntax or crontab-loading issue.

The environment problem (the #1 cause)

This is the reason most "why is my cron job not running" questions get posted. Cron runs jobs with a minimal environment. It is not your login shell, so it does not read .bashrc , .bash_profile , or your usual PATH . A script that works perfectly when you type it manually can fail under cron because a command it depends on can't be found.

Cron's default PATH is typically just /usr/bin:/bin . So a tool installed in /usr/local/bin (common for node , python3 , docker , aws ) simply isn't visible.

Fix it one of three ways:

  • Use absolute paths for everything. Instead of python3 script.py , write /usr/local/bin/python3 /home/user/script.py . Find the full path with which python3 .
  • Set PATH inside the crontab. Add a line at the top of your crontab so all jobs inherit it.
  • Source your environment at the start of the script. Useful when your job relies on many variables.
# Put this at the top of `crontab -e`
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash

0 2 * * * /home/user/backup.sh >> /tmp/cron.log 2>&1
Environment variables like API keys or database URLs that live in your shell profile won't exist under cron. Either define them in the crontab, load them from a file inside your script, or reference a dedicated env file with . /home/user/.env at the top of the script.

Schedule syntax mistakes

A cron schedule has five time fields. Getting one wrong means your job either never fires or fires at the wrong time. The fields, in order:

Field Range Notes
Minute 0-59 First field
Hour 0-23 24-hour clock
Day of month 1-31 Combine carefully with day of week
Month 1-12 Or Jan-Dec
Day of week 0-7 0 and 7 both mean Sunday

Common syntax slip-ups:

  • Using the wrong number of fields. Five fields, then the command. A six-field job (with seconds) is not standard cron and will be misread.
  • Confusing */5 with 5 . */5 * * * * means every 5 minutes; 5 * * * * means once an hour at minute 5.
  • Both day-of-month and day-of-week set. When both are specified (not * ), cron runs the job if either matches, which often surprises people.
  • Unescaped percent signs. A literal % in the command is treated as a newline by cron. Escape it as \% , which trips up jobs using date +%Y-%m-%d .

Validate your timing expression against the official crontab(5) man page if you're unsure how a field behaves.

Permission and executable issues

A "crontab permission denied" error usually means your script lacks the execute bit or cron can't read a file it needs. Walk through these:

  • Make the script executable: chmod +x /home/user/backup.sh .
  • Include a shebang. The first line must declare the interpreter, e.g. #!/bin/bash or #!/usr/bin/env python3 . Without it, cron doesn't know how to run the file.
  • Check who owns the job. A script that touches files in /root won't work from a user crontab. Match the crontab owner to the permissions the script needs.
  • Confirm cron access isn't restricted. Files named /etc/cron.allow and /etc/cron.deny control which users may schedule jobs. If your user is in cron.deny (or missing from cron.allow ), your crontab is ignored entirely.

Also watch for line-ending problems. If you wrote the script on Windows, it may have carriage-return characters ( \r ) that break the shebang. Clean them with sed -i 's/\r$//' script.sh or run the file through dos2unix .

Fast test trick: Set the job to * * * * * (every minute), point output to a log, wait 60 seconds, then read the log. Once it works, restore your real schedule. This turns hours of guessing into a one-minute feedback loop.

Quick troubleshooting checklist

Run through these in order and you'll catch the vast majority of cron failures:

  1. Is the cron daemon running? ( systemctl status cron )
  2. Is the job actually in the crontab? ( crontab -l )
  3. Are you looking at the right user's crontab (yours vs. root)?
  4. Is output being logged with >> file 2>&1 ?
  5. Does the system log show cron attempting the job?
  6. Do all commands use absolute paths, or is PATH set in the crontab?
  7. Is the schedule syntax correct (five fields, escaped % )?
  8. Is the script executable with a valid shebang?
  9. Is your user allowed by cron.allow / cron.deny ?
  10. Does the file have Unix line endings?
Troubleshooting a cron job that is not running with online developer tools

Need help with your cron schedule?

Use our free online cron expression builder to create, test, and validate your cron schedules in seconds. Perfect for developers who want to avoid syntax errors and ensure their jobs run at the right time.

Try Our Free Tools →

Because cron uses a minimal environment that skips your shell profile. Your PATH and environment variables aren't loaded, so commands in /usr/local/bin or custom variables go missing. Use absolute paths, set PATH in the crontab, or source an env file inside your script.

Redirect output to a log file by appending >> /tmp/cron.log 2>&1 to the job. The 2>&1 captures error messages too. You can also check /var/log/syslog or run journalctl -u cron to confirm cron attempted the job at all.

It usually means the script isn't executable or your user is blocked from using cron. Run chmod +x on the script, add a valid shebang line, and check that your username isn't listed in /etc/cron.deny or missing from /etc/cron.allow .

No. When you save with crontab -e , cron detects the change automatically and picks up the new schedule. You only need to restart the daemon if the service itself stopped or if you edited system files under /etc/cron.d in some setups.

Most often it's a syntax slip or a timezone mismatch. Confirm the five fields are in the right order and remember cron uses the system timezone, not your local one. Check with timedatectl and set CRON_TZ= at the top of the crontab if you need a specific zone.