Cron Job Not Running? Why Your Crontab Isn't Working and How to Fix It
You set up a cron job. You waited. Nothing happened. You waited longer. Still nothing. You checked and double-checked the schedule. You edited the crontab again. You googled it. You asked ChatGPT. It still isn't running.
Cron is one of the most useful tools on a Linux server, and also one of the most frustrating to debug. Here's why it's probably not working, and how to fix it.
What cron actually is
Cron is like an alarm clock for your server. You tell it "run this command every day at 3 AM" or "run this script every five minutes," and it does it automatically in the background. No human needed.
People use cron for things like:
- Running database backups every night
- Sending out email reports every Monday morning
- Cleaning up temporary files once a week
- Checking if a service is still alive and restarting it if it's down
You set it up by editing something called a crontab (cron table) — a list of scheduled commands. You edit it with crontab -e, add a line, save, and cron is supposed to handle the rest.
Supposed to.
The silent failure problem
Here's the thing about cron that nobody warns you about: it fails silently.
When you run a command in your terminal and something goes wrong, you see the error immediately. Red text, a traceback, a "file not found" message — something tells you what happened.
Cron runs in the background with no terminal attached. If your job fails, cron doesn't pop up a notification. It doesn't send you an email (unless you specifically configured that). It doesn't log the error anywhere obvious. It just... doesn't run. Or it runs, hits an error, and quietly moves on.
Your cron job can fail every single day for a month and you would never know unless you set up logging yourself. This is the #1 reason people think cron "isn't working" — it might actually be running and failing, but you can't see the failure.
Crontab syntax: the schedule format
Before we get into debugging, let's make sure the schedule itself is right. Every crontab line has five time fields followed by the command to run:
# minute hour day-of-month month day-of-week command
0 3 * * * /path/to/script.sh
That example means "at minute 0 of hour 3 (3:00 AM), every day of every month, regardless of what day of the week it is, run this script."
Here are a few more examples:
# Every 5 minutes
*/5 * * * * /usr/bin/python3 /home/deploy/check_health.py
# Every Monday at 9 AM
0 9 * * 1 /home/deploy/send_report.sh
# First day of every month at midnight
0 0 1 * * /home/deploy/cleanup.sh
# Every day at 2:30 PM
30 14 * * * /usr/bin/node /home/deploy/myapp/task.js
Common mistakes in the schedule:
- Forgetting it's 24-hour time. 2 PM is 14, not 2. If you put
0 2 * * *thinking it means 2 PM, it runs at 2 AM. - Day of week numbering. Sunday is 0 (or 7), Monday is 1, Saturday is 6.
- Spacing and formatting. Each field must be separated by spaces. No tabs, no extra characters, no trailing spaces on the line.
If you want to double-check your schedule, search for "crontab guru" — it's a website that lets you paste a cron expression and see in plain English when it will run.
The most common reasons your cron job isn't working
1. The PATH trap
This is the single most common reason cron jobs fail, and it trips up almost everyone.
When you open your terminal and type python3 myscript.py, it works. Your terminal knows where python3 is because your shell has a PATH variable — a list of directories to search for executables. Your terminal might search /usr/local/bin, /usr/bin, /home/youruser/.local/bin, and a dozen other places.
Cron has almost no PATH. Its default PATH is typically just /usr/bin:/bin. That's it. If your Python is installed at /usr/local/bin/python3 or your Node is managed by nvm at some deeply nested path, cron has no idea where to find it.
This is why a command works perfectly in your terminal but silently fails in cron. Cron tries to run python3, can't find it, and gives up.
Fix: Always use full, absolute paths to every executable in your cron jobs. Don't write:
*/5 * * * * python3 myscript.py
Write:
*/5 * * * * /usr/bin/python3 /home/deploy/myscript.py
To find the full path to any command, type which python3 or which node in your terminal. Whatever it prints, use that exact path in your crontab.
2. Relative paths instead of absolute paths
This is the PATH trap's cousin. Even if cron finds your executable, your script might reference files using relative paths like ./data/output.csv or config.json. In your terminal, those resolve relative to whatever directory you're in. In cron, the working directory is typically your home directory or / — not the directory your script lives in.
So your script runs, tries to open ./data/output.csv, can't find it, and fails.
Fix: Use absolute paths everywhere inside your scripts, or cd into the right directory first:
*/5 * * * * cd /home/deploy/myapp && /usr/bin/python3 task.py
3. Your script isn't executable
If your crontab line runs a script directly (not through an interpreter), the script file needs to be marked as executable. If it isn't, cron can't run it.
# This won't work if cleanup.sh isn't executable:
0 3 * * * /home/deploy/cleanup.sh
# Fix: make it executable
chmod +x /home/deploy/cleanup.sh
Alternatively, call the interpreter explicitly and it doesn't matter whether the file is executable:
0 3 * * * /bin/bash /home/deploy/cleanup.sh
4. Wrong user's crontab
Every user on a Linux system has their own crontab. If you set up the job as your regular user but the script needs root permissions, it won't work. If you set it up as root but the script depends on things in your regular user's home directory, that won't work either.
crontab -e edits your current user's crontab. sudo crontab -e edits root's crontab. These are completely separate lists.
Run crontab -l (and sudo crontab -l) to see what's actually scheduled for each user. You might find your job is in the wrong one.
5. Environment variables aren't available
Just like with systemd services, cron runs in a stripped-down environment. It doesn't read your .bashrc, .profile, or .bash_profile. Any environment variables you set in those files — DATABASE_URL, API_KEY, JAVA_HOME — simply don't exist when cron runs your job.
Fix: Either set the variables directly in the crontab (before your job lines):
DATABASE_URL=postgres://user:pass@localhost/mydb
API_KEY=abc123
*/5 * * * * /usr/bin/python3 /home/deploy/myapp/task.py
Or source your profile at the start of the command:
*/5 * * * * source /home/deploy/.profile && /usr/bin/python3 /home/deploy/myapp/task.py
6. Syntax errors in the crontab
Cron is unforgiving about formatting. A stray character, a missing field, or a typo in the schedule will cause the entire line to be ignored — silently, of course.
Common syntax problems:
- Only four time fields instead of five
- Using commas with spaces (
1, 15instead of1,15) - Special characters that the shell interprets differently (like
%— cron treats%as a newline, so if your command has one, it gets split in half) - Forgetting the newline at the end of the file — some versions of cron require a blank line at the bottom of the crontab or they'll ignore the last entry
Fix: If your command contains a % sign (common in date formats), escape it with a backslash:
# Wrong — cron will break on the %
0 3 * * * /usr/bin/python3 /home/deploy/backup.py --date=$(date +%Y-%m-%d)
# Right — escape the percent signs
0 3 * * * /usr/bin/python3 /home/deploy/backup.py --date=$(date +\%Y-\%m-\%d)
How to debug a cron job that isn't running
Step 1: Verify your crontab is saved
Run crontab -l and make sure your job is actually listed. If it isn't there, you either edited the wrong user's crontab, or your edits didn't save (some editors are finicky with the temp file cron uses).
Step 2: Check the cron log
Most Linux systems log cron activity. Check one of these:
# On Ubuntu/Debian
grep CRON /var/log/syslog
# On CentOS/RHEL/Fedora
cat /var/log/cron
This will show you whether cron even attempted to run your job. If you see entries like CMD (/home/deploy/myscript.sh), cron ran it — the problem is inside the script. If you see nothing, cron never tried, which means the schedule or syntax is wrong.
Step 3: Redirect output to a log file
This is the most important debugging step. Add output redirection to your cron command so you can see what happened:
*/5 * * * * /usr/bin/python3 /home/deploy/task.py >> /home/deploy/cron.log 2>&1
The >> appends stdout to the log file. The 2>&1 redirects stderr (error messages) to the same file. Now when the job runs, any output or errors get written to cron.log where you can actually read them.
After the next scheduled run, check the log:
cat /home/deploy/cron.log
If the file is empty, the job didn't run at all. If it has error messages, you know exactly what to fix.
Step 4: Test the exact command manually
Copy the command from your crontab and run it in a terminal that mimics cron's environment:
env -i /bin/sh -c '/usr/bin/python3 /home/deploy/task.py'
The env -i clears all environment variables, giving you a blank slate similar to what cron uses. If the command fails here, it will fail in cron too.
Why AI gives bad cron advice
If you asked an AI to set up a cron job, there's a good chance it gave you something that doesn't work. Here's what it typically gets wrong:
- Relative paths everywhere. The AI writes
python3 myscript.pyinstead of/usr/bin/python3 /home/deploy/myscript.py. It works in a terminal demo. It fails in cron. - No mention of the PATH issue. The AI doesn't warn you that cron has a different PATH than your shell. This is the most common cron problem and AI routinely ignores it.
- No output redirection. The AI gives you a bare cron line with no
>> logfile 2>&1. So when it fails, you have no way to see why. - No mention of environment variables. If your script needs
DATABASE_URLorAPI_KEY, the AI doesn't tell you those won't be available in cron's environment. - Generates plausible-looking schedules. The syntax looks right. The paths look right. Everything looks right. And then it silently doesn't work because of one of the issues above, and you spend an hour trying to figure out what you did wrong when the AI's answer was wrong from the start.
Cron is one of those tools where the gap between "looks correct" and "actually works" is huge. And that gap is exactly where AI falls short — it generates things that look right but can't verify they actually work on your specific system.
A complete, working cron setup
Here's what a properly configured cron job looks like, with all the common pitfalls addressed:
# Edit your crontab
crontab -e
# Add this line (runs every day at 3 AM):
0 3 * * * cd /home/deploy/myapp && /usr/bin/python3 /home/deploy/myapp/backup.py >> /home/deploy/myapp/cron.log 2>&1
What this does right:
cd /home/deploy/myapp— sets the working directory so relative file references inside the script work/usr/bin/python3— full absolute path to the interpreter, no reliance on PATH/home/deploy/myapp/backup.py— full absolute path to the script>> /home/deploy/myapp/cron.log 2>&1— captures all output and errors to a log file you can actually read
After saving, verify it's there:
crontab -l
Then wait for the scheduled time, and check your log file. If the log has output, your job is running. If the log has errors, you know what to fix. If the log doesn't exist, go back to the cron system log and see if cron even attempted to run it.
Cron still not cooperating?
Silent failures are the worst kind. You can't fix what you can't see, and cron is designed to hide its problems from you. Press the MeatButton and a real expert will look at your crontab, check the logs, trace the PATH, and get your job running. The first one is free.
Get MeatButton