Developer guide
Cron Job Not Running? The 9 Real Causes & Fixes
The crontab line looks perfect, the command runs when you paste it into a terminal — and yet the job never fires. Nine times out of ten the difference is theenvironment, not the expression. Here is the exact checklist to find out which one is biting you.
Written by Benjamin Rotshtein
Updated
Why is my cron job not running?
Nine causes cover almost every case: the cron daemon is down or the crontab entry is missing, cron strips your PATH and shell profile, the script is not executable, output goes nowhere, the script errors silently, mtime checking skips edited files, the schedule or server clock is wrong, and DST gaps swallow a run. Each has a check and a one-line fix in this guide.
- Why does cron fail when the terminal works?
- Cron runs with a minimal environment (PATH /usr/bin:/bin, no aliases, no .bashrc). Any tool you rely on that lives outside that PATH simply “does not exist” to cron.
- Does cron read .bashrc?
- No. The profile files load only for interactive or login shells. Cron launches your script through /bin/sh or its shebang — the environment you configured never loads.
- Fastest diagnostic?
- Redirect all output to a file and let it run:
* * * * * /tmp/job.sh >> /tmp/job.log 2>&1— the log tells you almost instantly.
1. The daemon is not running (or the crontab entry is missing)
Before anything else, confirm the machine actually has a scheduler. On systemd hosts: systemctl status cron (sometimes crond). On a container, cron is often not installed at all. Then verify your line survived the editor: crontab -l. A crontab whose last line has no trailing newline is silently ignored — and a regex typo like * * * * (four fields) makes cron reject the whole entry with a quiet error.
2. Missing PATH — the classic “works in terminal” bug
Your login shell inherits a long PATH from /etc/profile and ~/.bashrc. Cron does not: it uses a default of /usr/bin:/bin plus HOME and LOGNAME. If your script calls node, python3, docker or anything in a version-manager install, cron cannot find it. Fix anywhere in the script:
#!/bin/bash export PATH="/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:$PATH" export NVM_DIR="$HOME/.nvm" # if your tools live behind a version manager [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" echo "PATH: $PATH" >> /tmp/job.log
3. .bashrc and .bash_profile never load
Cron does not start a login shell, so nothing in your dotfiles runs. If your script depends on aliases, functions or variables defined in ~/.bashrc, they are absent. Either source them explicitly at the top of the script, or export every value the job needs. Never rely on the interactive shell’s state.
4. The script is not executable
If the crontab calls * * * * * /home/me/backup.sh and the file lacks the execute bit, cron logs permission denied. Run chmod +x /home/me/backup.sh — or call it through an interpreter: * * * * * /bin/bash /home/me/backup.sh, which sidesteps chmod entirely.
5. Output goes nowhere — add redirection
Because cron has no terminal, error output is mailed to the user’s local mailbox by default — which nobody reads, and which does not exist on many minimal server images. Add redirection so failures write where you can see them:
MAILTO="" * * * * * /home/me/backup.sh >> /home/me/backup.log 2>&1
Every run appends its output and its errors to the same log. Next time the job looks stuck, you read the log instead of guessing.
6. The script errors on the way out
Watch the log for the real failure: a missing input file, a folder you expected to exist (cron has no “current directory” — it starts at HOME), or a tool that exists interactively but not in cron’s PATH. A script that exits nonzero is indistinguishable from one that never ran unless you log the exit code:
#!/bin/bash /opt/tool/job.sh echo "exit: $? at $(date)" >> /tmp/job.log
7. The mtime trap: editing a script while it is scheduled
In some cron implementations, if a script’s modification time is newer than its last execution time, cron assumes the file changed mid-run and skips that invocation — silently. It is the classic “I edited the script and now it stopped running” symptom. Fix: touch the file so mtime is old (or re-run once manually), or restart the cron daemon. On many systems simply logging out and back in resets the relevant state.
8. Schedule or clock mismatch
Check the field order minute hour day-of-month month day-of-week — the most common human error is writing * * * * 5 for “every Friday” but also expecting hours/minutes to be 0 9, or putting the hour in the day field. And remember cron uses the server’s clock, not your browser’s. A “not running” job at 09:00 your time may simply be 06:00 UTC on the box.
9. Time zone and DST gaps
If the server observes daylight saving time, a job pinned to the skipped hour never fires that day, and a job in the repeated hour can run twice. The DST-proof baseline is UTC. This is a separate rabbit hole worth its own guide — which we already wrote: Cron & Time Zones: How DST Messes Up Schedules.
Frequently asked questions
Why is my cron job not running the bash script?
Usually one of three things: crond is not running or the entry is malformed, the script's interpreter or PATH is missing (cron uses a minimal environment), or the script file is not executable. Cheap habits that prevent most of these: an explicit /bin/bash shebang, absolute paths to every binary inside the script, chmod +x on the file, and 2>&1 output redirection written to a log you can inspect.
Does cron read .bashrc?
No. Cron launches your command with /bin/sh (or the shebang interpreter) in a near-empty environment: a default PATH of /usr/bin:/bin, HOME and LOGNAME, and little else. .bashrc and .bash_profile load only when an interactive or login shell starts, which a cron run is not. Load the profile yourself inside the script, or export every PATH you need at the top of it.
Why does cron fail but the command works in my terminal?
Because your terminal shell is a login, interactive shell with your full profile loaded, while cron runs in a minimal environment with a different PATH and no aliases. The command works in bash because your ~/.bashrc put node, docker or python on PATH — cron never sees that. Put absolute paths or explicit exports in the cron script and it will run identically.
How do I know why my cron job is not running?
Check five things in order: 1) the crontab contains the entry (crontab -l), 2) the cron daemon is active (systemctl status cron, ps aux | grep cron), 3) any missed-run mail or a log file the job writes, 4) the script runs when you execute it manually, 5) your editor's trailing newline — cron silently ignores the last line of a crontab with no newline at the end.
Does cron re-run a script if I edit it?
Not automatically, and on many systems (classic Vixie cron, busybox) the mtime of a script matters: if the file's modification time is newer than its execution time, cron assumes the file was edited mid-run and skips that invocation. Log out and back in, or ssh with an environment reset, so the system time context matches. Restarting crond also clears the state.
How do I find cron's error output when a job fails?
Redirect everything to a file while you debug: * * * * * /tmp/job.sh >> /tmp/job.log 2>&1. Without redirection, cron tries to mail output to the local user mailbox, which does not exist on many minimal images — so errors vanish. The log file makes the failure visible and is the fastest path to a fix.
Build a schedule that survives
Once your script actually runs, generate the crontab line from plain English instead of counting fields by hand — and preview the next fire times so a clock misconception never wastes another day.