Cron Jobs Not Running? Check the Env and the Path
Cron jobs failing silently? Usually a missing PATH or environment variable. Here's the fix and why it works.
Yeah, you set up a cron job, checked the syntax twice, and it just sits there doing nothing. Been there. Let's cut the crap and get it running.
The Quick Fix: Add a Proper PATH and Shebang
Open your crontab with crontab -e. Find the failing line. At the top of the crontab file (before any job lines), add this:
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
SHELL=/bin/bash
Then, in your script itself, make sure the first line is a shebang:
#!/bin/bash
Now test the job by running it manually from the cron environment. Use this to simulate cron's minimal shell:
env -i HOME=$HOME PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin SHELL=/bin/bash /bin/bash -l -c 'your-command-here'
If that runs, your job will run in cron. If not, debug further. Nine times out of ten, this is the fix.
Why This Works
Cron runs your job in a stripped-down environment. No PATH, no aliases, no user-specific variables. Your script might work fine in your interactive shell, but fail silently in cron because python or docker-compose or whatever isn't found. Adding the PATH at the top of the crontab gives cron the same search directories your login shell uses.
The shebang is critical too. Without it, cron uses /bin/sh as the interpreter, which might be a different shell than your script expects. I had a client last month whose backup script used bash-specific array syntax. Worked fine when they ran it by hand, but cron ran it under dash (Debian's default sh) and it failed with a cryptic error. One shebang line fixed it.
Also, cron doesn't source your .bashrc or .profile. So anything you set there—like environment variables for a database connection or a language runtime—won't be available. That's why I put critical variables directly in the crontab or inside the script.
Less Common Variations of the Same Issue
1. Script Path Problem
You wrote 0 3 * * * my_script.sh. Cron doesn't know where my_script.sh lives. Use the full path:
0 3 * * * /home/user/scripts/my_script.sh
2. Missing Output Redirection
Cron captures stdout and stderr and emails them to you if mail is set up. But if you never check mail, you miss the error. Force output to a log file:
0 3 * * * /home/user/scripts/backup.sh >> /home/user/logs/backup.log 2>&1
Now check that log after the job should have run. I've spent hours debugging cron jobs only to find the error was sitting in an unread mailbox.
3. Permission Issues
Your script needs execute permission. Run chmod +x /path/to/script.sh. Also, cron runs as the user who owns the crontab. If your script needs to write to a directory owned by root, it'll fail. I once had a job that backed up databases—ran fine as root via terminal, but cron (running as a non-root user) couldn't write to /var/backups. Changed the output directory to /home/user/backups, problem gone.
4. Environment-Specific Variables
Python virtual environment? Ruby gems? Node modules? Cron doesn't know about them. Either source the activation script inside your cron job:
0 3 * * * source /home/user/venv/bin/activate && /home/user/scripts/myscript.py
Or set the full path to the interpreter:
0 3 * * * /home/user/venv/bin/python /home/user/scripts/myscript.py
5. Timezone Conflicts
If your server uses UTC but you expected local time, the job runs at the wrong hour. Check with timedatectl or cat /etc/timezone. I had a job set to run at 2 AM for a client, but the server was UTC, so it ran at 2 AM UTC which was 10 PM local. Adjusted the cron time to match local timezone.
Prevention: How to Make Sure Jobs Don't Fail Again
From now on, do this for every new cron job:
- Start every crontab with
PATHandSHELLlines. Copy-paste the ones from the fix above. - Always use full paths in the crontab command.
- Redirect output to a log file.
- Test the job using the
env -icommand I gave you—that's the single best way to catch cron-specific failures. - Set up a simple health check: have the job write a timestamp to a text file. If the file doesn't update, you know something's wrong.
One more thing: if you're still stuck, check /var/log/syslog or /var/log/cron for cron errors. The message will say something like (user) CMD (command) - exit code 127, which usually means command not found. That's your path problem again.
Cron is stupid simple once you know its quirks. Set the environment explicitly, test like cron does, and you'll never waste hours on a silent failure again.
Was this solution helpful?