Why Is My Cron Job Not Running? The 7 Most Common Errors and How to Fix Them
Your script works perfectly when you run it manually. You add it to cron. You wait. Nothing happens.
No output. No errors. No results. Just... silence.
Sound familiar? You're not alone. Cron jobs fail silently for everyone. The difference is knowing where to look and what to fix.
This guide covers the 7 most common reasons cron jobs fail—ranked by how often they actually happen—plus the exact commands to diagnose and fix each one.
Let's debug your cron job.
Error #1: Wrong File Paths (The #1 Killer)
The Problem
This happens 60% of the time. Your script works manually because your shell knows where everything is. Cron doesn't.
When you run ./backup.sh, your shell uses your $PATH to find executables. Cron runs with a minimal environment and can't find anything that isn't specified with an absolute path.
Symptoms
# Your cron job
0 2 * * * backup.sh
# What you see
Nothing. No output. No errors. Script never runs.
The Fix
Use absolute paths for EVERYTHING:
Wrong:
0 2 * * * backup.sh
0 2 * * * python script.py
0 2 * * * mysqldump database > backup.sql
Right:
0 2 * * * /usr/local/bin/backup.sh
0 2 * * * /usr/bin/python3 /home/user/script.py
0 2 * * * /usr/bin/mysqldump database > /var/backups/backup.sql
How to Find Absolute Paths
# Find where a command is located
which python3
# Output: /usr/bin/python3
which mysqldump
# Output: /usr/bin/mysqldump
which node
# Output: /usr/local/bin/node
Quick Test
Before adding to cron, test with cron's environment:
# Run with minimal environment (like cron does)
env -i HOME=$HOME /usr/local/bin/backup.sh
# If it fails here, it will fail in cron
Pro Tip: Set PATH in Crontab
Add this to the top of your crontab:
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 2 * * * /usr/local/bin/backup.sh
This gives cron a better PATH, but still use absolute paths for scripts.
Error #2: Permission Denied (The Silent Killer)
The Problem
Your script isn't executable. Cron tries to run it, gets "permission denied," and gives up silently.
Symptoms
# Check your cron logs
grep CRON /var/log/syslog
# You see
CRON[1234]: (user) CMD (/home/user/backup.sh)
# But nothing happens
The Fix
Make your script executable:
# Check current permissions
ls -l /home/user/backup.sh
# Output: -rw-r--r-- (NOT executable)
# Fix it
chmod +x /home/user/backup.sh
# Verify
ls -l /home/user/backup.sh
# Output: -rwxr-xr-x (Now executable)
The Right Permissions
# For scripts
chmod 755 /path/to/script.sh
# Owner: read, write, execute
# Others: read, execute
# For scripts with sensitive data
chmod 700 /path/to/script.sh
# Only owner can read/write/execute
Check File Ownership
# See who owns the file
ls -l /home/user/backup.sh
# If owned by root but running as user
sudo chown user:user /home/user/backup.sh
Don't Forget Shebang
Start every script with:
#!/bin/bash
# or
#!/usr/bin/env python3
# or
#!/usr/bin/env node
Without this, cron doesn't know how to execute your script.
Error #3: Environment Variables Missing
The Problem
Your script relies on environment variables that exist in your shell but not in cron's minimal environment.
Symptoms
Script works manually:
$ ./backup.sh
✓ Database backed up successfully
Same script in cron:
# Cron log shows
mysqldump: command not found
# or
Error: API_KEY not set
The Fix
Option 1: Set variables in crontab
# At the top of your crontab
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
HOME=/home/user
API_KEY=your_key_here
0 2 * * * /usr/local/bin/backup.sh
Option 2: Source environment in script
#!/bin/bash
# Load environment variables
source /home/user/.bashrc
# or
source /home/user/.env
# Now your script has access to all variables
/usr/bin/mysqldump...
Option 3: Set variables in the cron job
0 2 * * * API_KEY=xyz DB_USER=admin /usr/local/bin/backup.sh
Debug: Check What Cron Sees
Create a test cron job to see the environment:
# Add to crontab
* * * * * env > /tmp/cron-env.txt
# Wait one minute, then check
cat /tmp/cron-env.txt
# Compare to your normal environment
env > /tmp/normal-env.txt
diff /tmp/cron-env.txt /tmp/normal-env.txt
You'll be shocked how little cron has access to.
Common Missing Variables
PATH- Where to find executablesUSER- Current usernameHOME- Home directoryLANG- Language settings- API keys and credentials
- Database passwords
- Custom application variables
Error #4: Cron Syntax Errors
The Problem
Your cron expression is malformed. Cron silently ignores the entire line.
Symptoms
# You added this to crontab
0 2 * * * 1-5 /usr/local/bin/backup.sh
# Nothing happens. No errors in logs.
Common Syntax Mistakes
1. Wrong field count
# Wrong (6 fields - one too many)
0 2 * * * 1-5 /usr/local/bin/backup.sh
# Right (5 fields + command)
0 2 * * 1-5 /usr/local/bin/backup.sh
2. Invalid range
# Wrong (hour 25 doesn't exist)
0 25 * * * /usr/local/bin/backup.sh
# Right
0 23 * * * /usr/local/bin/backup.sh
3. Typos in special characters
# Wrong (using | instead of /)
*/15 * * * * /usr/local/bin/check.sh
# Right
*/15 * * * * /usr/local/bin/check.sh
4. Invalid day/month combinations
# Wrong (February 31st doesn't exist)
0 0 31 2 * /usr/local/bin/monthly.sh
# Be careful with day-of-month + month combinations
The Fix
Before adding to crontab, validate your syntax:
-
Use our cron expression validator - Paste your expression and see if it's valid
-
Check with cron syntax checker:
# Many modern systems validate on save
crontab -e
# If there's a syntax error, you'll see a warning
- Test with a simple command first:
# Add a test job that runs every minute
* * * * * echo "It works!" >> /tmp/crontest.txt
# Wait a minute, check the file
cat /tmp/crontest.txt
# If you see output, cron is working
Quick Syntax Reference
# Field order
* * * * * command
│ │ │ │ │
│ │ │ │ └─ Day of Week (0-6, Sunday=0)
│ │ │ └─── Month (1-12)
│ │ └───── Day of Month (1-31)
│ └─────── Hour (0-23)
└───────── Minute (0-59)
# Valid examples
*/15 * * * * # Every 15 minutes
0 */6 * * * # Every 6 hours
0 9-17 * * 1-5 # Business hours, weekdays
0 0 1,15 * * # 1st and 15th of month
Error #5: Output Not Being Captured
The Problem
Your cron job IS running, but you can't see the output. You assume it's not working.
Symptoms
# Cron log shows it ran
grep CRON /var/log/syslog
# Output: CRON[1234]: (user) CMD (/usr/local/bin/backup.sh)
# But you don't see any results
The Fix
Redirect output to a log file:
# Capture both stdout and stderr
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Only capture errors
0 2 * * * /usr/local/bin/backup.sh 2>> /var/log/backup-errors.log
# Separate logs for stdout and stderr
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>> /var/log/backup-error.log
Understanding redirections:
>>- Append to file (don't overwrite)>- Overwrite file2>&1- Redirect stderr (errors) to stdout2>>- Append errors to file> /dev/null 2>&1- Discard all output
Enable Email Notifications
Cron can email you output:
# At top of crontab
MAILTO=your-email@example.com
# Now cron will email you any output
0 2 * * * /usr/local/bin/backup.sh
Disable email:
MAILTO=""
# or redirect to null
0 2 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
Add Logging to Your Script
#!/bin/bash
LOG_FILE="/var/log/backup.log"
echo "[$(date)] Starting backup..." >> $LOG_FILE
# Your backup commands
if /usr/bin/mysqldump database > backup.sql; then
echo "[$(date)] Backup successful" >> $LOG_FILE
else
echo "[$(date)] ERROR: Backup failed!" >> $LOG_FILE
exit 1
fi
echo "[$(date)] Backup complete" >> $LOG_FILE
Check if Job Actually Ran
# View recent cron executions
grep CRON /var/log/syslog | tail -20
# On some systems
journalctl -u cron -n 50
# See what user's cron jobs ran
grep "CRON\[.*\]: (user)" /var/log/syslog
Error #6: Cron Service Not Running
The Problem
The cron daemon isn't running at all. None of your cron jobs will execute.
Symptoms
# Your crontab looks fine
crontab -l
# Shows your jobs
# But nothing ever runs
# No entries in /var/log/syslog
The Fix
Check if cron is running:
# On systemd systems (Ubuntu, Debian, CentOS 7+)
systemctl status cron
# or
systemctl status crond
# On older systems
service cron status
If it's not running, start it:
# Start cron
sudo systemctl start cron
# Enable it to start on boot
sudo systemctl enable cron
# Verify it's running
systemctl is-active cron
On macOS:
# Check if cron is loaded
sudo launchctl list | grep cron
# Load cron if needed
sudo launchctl load -w /System/Library/LaunchDaemons/com.vix.cron.plist
Check Cron Logs
# Ubuntu/Debian
tail -f /var/log/syslog | grep CRON
# CentOS/RHEL
tail -f /var/log/cron
# Check if any cron jobs have run recently
grep CRON /var/log/syslog | tail -50
Verify Cron Daemon
# See if crond process is running
ps aux | grep cron
# Should show something like:
# root 1234 0.0 0.0 /usr/sbin/cron -f
Error #7: Script Works in Terminal, Fails in Cron
The Problem
Your script runs perfectly when you execute it manually but fails mysteriously in cron. This is the most frustrating error because everything LOOKS right.
Why This Happens
When you run a script manually:
- Your shell loads
.bashrc,.bash_profile, etc. - You have a full environment with all variables
- Your PATH includes everything
- You're running from a specific directory
When cron runs your script:
- Minimal environment (no .bashrc loading)
- Limited PATH
- Different working directory
- Different user (sometimes)
Symptoms
# Works manually
$ /usr/local/bin/backup.sh
✓ Success!
# Fails in cron
0 2 * * * /usr/local/bin/backup.sh
# No output, or errors in log
The Fix
1. Simulate cron's environment:
# Run with cron's minimal environment
env -i HOME=$HOME SHELL=/bin/sh /usr/local/bin/backup.sh
# If it fails here, you found the problem
2. Make script self-contained:
#!/bin/bash
# Set environment explicitly
export PATH=/usr/local/bin:/usr/bin:/bin
export HOME=/home/user
cd /home/user/project || exit 1
# Load any needed environment variables
if [ -f /home/user/.env ]; then
source /home/user/.env
fi
# Now run your commands
/usr/bin/python3 script.py
3. Use absolute paths everywhere:
#!/bin/bash
# Wrong
mysql -u root database < backup.sql
# Right
/usr/bin/mysql -u root database < /var/backups/backup.sql
4. Specify working directory in cron:
# Change to directory before running
0 2 * * * cd /home/user/project && /usr/local/bin/backup.sh
# Or set it in the script
0 2 * * * /usr/local/bin/backup.sh
5. Debug by comparing environments:
Create a debug script:
#!/bin/bash
echo "PATH: $PATH"
echo "HOME: $HOME"
echo "USER: $USER"
echo "PWD: $PWD"
echo "SHELL: $SHELL"
which python3
which mysql
ls -la
Run it manually and via cron:
# Manual
./debug.sh > /tmp/manual-env.txt
# Cron
* * * * * /path/to/debug.sh > /tmp/cron-env.txt
# Compare
diff /tmp/manual-env.txt /tmp/cron-env.txt
The differences tell you what's missing.
Bonus: Advanced Debugging Techniques
Enable Detailed Logging
Wrap your entire script in verbose mode:
#!/bin/bash
set -x # Print every command before executing
set -e # Exit on any error
# Your script here
/usr/bin/mysqldump...
Test Cron Every Minute
While debugging, make the job run every minute:
# Change this:
0 2 * * * /usr/local/bin/backup.sh
# To this temporarily:
* * * * * /usr/local/bin/backup.sh >> /tmp/debug.log 2>&1
# Check log after 1-2 minutes
tail -f /tmp/debug.log
# Once working, change back to correct schedule
Create a Wrapper Script
Wrap complex commands in a script that handles all environment setup:
#!/bin/bash
# /usr/local/bin/cron-wrapper.sh
# Setup environment
export PATH=/usr/local/bin:/usr/bin:/bin
source /home/user/.bashrc
# Enable logging
exec 1> >(logger -s -t $(basename $0)) 2>&1
# Run the actual command
"$@"
Use it in cron:
0 2 * * * /usr/local/bin/cron-wrapper.sh /home/user/backup.sh
Check System Resources
Sometimes cron jobs fail due to resource limits:
# Check available disk space
df -h
# Check memory
free -h
# Check CPU load
uptime
# Check if system is swapping
vmstat 1 5
The Ultimate Cron Debugging Checklist
When your cron job doesn't work, check these in order:
1. Is cron running?
systemctl status cron
2. Is your cron syntax valid?
- Use our cron validator
- Check with
crontab -e(it validates on save)
3. Does the cron job appear in logs?
grep CRON /var/log/syslog | grep username
4. Is the script executable?
ls -l /path/to/script.sh
chmod +x /path/to/script.sh
5. Are you using absolute paths?
which command-name
# Use the full path shown
6. Can you run it manually?
/path/to/script.sh
7. Can you run it with cron's environment?
env -i HOME=$HOME /path/to/script.sh
8. Is output being captured?
# Add logging
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>&1
9. Are environment variables set?
# Add to crontab:
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
10. Are there any missing dependencies?
# Check if all commands exist
which python3
which mysqldump
# etc.
Quick Fixes That Solve 90% of Problems
Most cron issues come down to these three fixes:
Fix #1: The Minimal Working Template
Use this template for ALL cron jobs:
# Environment
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
# Job with full logging
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
Fix #2: The Bulletproof Script Header
Start every script like this:
#!/bin/bash
set -e # Exit on error
set -u # Exit on undefined variable
set -o pipefail # Catch errors in pipes
# Set environment
export PATH=/usr/local/bin:/usr/bin:/bin
cd "$(dirname "$0")" || exit 1
# Your code here
Fix #3: The Debug Command
When all else fails, capture EVERYTHING:
* * * * * /path/to/script.sh >> /tmp/debug.log 2>&1 || echo "Exit code: $?" >> /tmp/debug.log
This shows:
- All output
- All errors
- Exit code if it fails
Still Not Working?
If you've tried everything and your cron job still won't run:
Use Our Tools
-
Validate your cron syntax with our cron generator
- Build expressions visually
- See exactly when they'll run
- No syntax errors
-
Decode existing expressions with our decoder
- Paste your cron string
- Get human-readable explanation
- Verify it does what you think
Get Professional Help
Check these resources:
- Your system logs:
/var/log/syslogor/var/log/cron - Cron documentation:
man crontab - Stack Overflow with your specific error message
Nuclear Option: Start Over
Sometimes it's faster to rebuild than debug:
- Remove the problematic job:
crontab -e - Test your script manually until it works perfectly
- Add it back with proper logging
- Monitor the logs to verify execution
Prevent Future Problems
Before Adding Any Cron Job:
- ✅ Test the script manually
- ✅ Test with minimal environment (
env -i) - ✅ Use absolute paths everywhere
- ✅ Add comprehensive logging
- ✅ Validate the cron syntax
- ✅ Start with frequent execution (every minute)
- ✅ Monitor logs to verify it works
- ✅ Change to actual schedule once confirmed
Standard Script Template
Use this template for reliable cron scripts:
#!/bin/bash
# Exit on any error
set -euo pipefail
# Configuration
LOG_FILE="/var/log/$(basename $0 .sh).log"
LOCK_FILE="/tmp/$(basename $0 .sh).lock"
# Logging function
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
# Prevent concurrent execution
if [ -f "$LOCK_FILE" ]; then
log "ERROR: Script already running"
exit 1
fi
trap "rm -f $LOCK_FILE" EXIT
touch "$LOCK_FILE"
# Main script
log "Starting..."
# Your commands here
if /usr/bin/command1; then
log "Command 1 successful"
else
log "ERROR: Command 1 failed"
exit 1
fi
log "Complete"
This handles:
- Logging with timestamps
- Error handling
- Lock file to prevent overlaps
- Automatic cleanup
Your cron job should now be running. If it's still not working after checking all seven errors, the problem is probably something unique to your setup—time to dive into those logs.
Need to create a cron job without the debugging headache? Use our visual generator to build perfect expressions every time.
Stop debugging. Start automating.
Related Articles
Dive Deeper into Cron:
- The Ultimate Guide to Cron Jobs - Master cron from beginner to advanced
- Environment Variables in Cron Jobs - Detailed guide to fixing env issues
- Where Did My Cron Job Output Go? - Complete logging guide
Build Better Cron Jobs:
- Organize Your Crontab Like a Pro - Maintainability best practices
- Secure Your Cron Jobs from Attacks - Security hardening
- Fixing Permission Denied Errors in Cron Jobs - Solve permission issues
Practical Examples:
- Automate Database Backups with Cron - Production backup scripts
- 5 Real-World Cron Job Examples - Copy-paste ready examples
- 10 Essential Cron Jobs for Web Developers - Must-have automation