Cron Debugging: 7 Common Issues and How to Fix Them
Introduction
Cron jobs are the backbone of automated tasks in Unix-like systems, but they're notoriously difficult to debug when things go wrong. The silent nature of cron—running in the background without user interaction—means issues often go unnoticed until something breaks spectacularly.
This guide covers the seven most common cron debugging scenarios, complete with real-world examples and proven fixes.
1. Timezone Mismatch Issues
The Problem: Your cron job runs at the wrong time because the system timezone differs from your expectation.
Wrong approach:
# You think this runs at 9 AM EST, but server is in UTC
0 9 * * * /home/user/backup-script.sh
The fix:
# Check system timezone
timedatectl | grep "Time zone"
# Check cron's timezone interpretation
cat /etc/timezone
# If needed, set TZ variable in crontab
TZ=America/New_York
0 9 * * * /home/user/backup-script.sh
Debugging steps:
- Add
TZ=UTC dateto your cron job temporarily to log what time cron thinks it is - Compare with
datecommand output in your terminal - Remember: cron doesn't read your shell's TZ environment variable
2. Day-of-Month + Day-of-Week Logic Confusion
The Problem: The man 5 crontab page states these fields have OR logic, not AND—a common source of confusion.
The misconception:
# Many think this runs "on the 15th AND on Mondays"
# Actually runs "on the 15th OR on Mondays"
0 0 15 * 1 /home/user/script.sh
The fix for AND logic:
# Use a conditional check inside the script
0 0 * * 1 /home/user/script.sh # Runs every Monday
# Then inside script.sh:
#!/bin/bash
if [ "$(date +\%d)" -eq 15 ]; then
# Actually do the work
echo "Running because it's the 15th AND a Monday"
fi
3. Missing Environment Variables
The Problem: Cron runs with a minimal PATH and no user environment variables. Scripts work in terminal but fail in cron.
The error you'll see in logs:
/home/user/script.sh: line 5: python3: command not found
The fix (Option A - Set in crontab):
# Add at the top of crontab
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
# Or set full PATH for specific commands
0 * * * * PATH=/usr/local/bin:/usr/bin:/bin python3 /home/user/script.py
The fix (Option B - Set in script):
#!/bin/bash
# At the top of your script
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
# Now use commands
python3 /path/to/script.py
Debugging tip: Add env command to a cron job and check the output:
* * * * * env > /tmp/cron-env-debug.txt
4. Output Not Redirected
The Problem: Cron sends email for every output line, which either floods your mailbox or gets silently discarded if no MTA is configured.
The silent failure:
# This might email you or do nothing
0 2 * * * /home/user/backup.sh
The fix:
# Redirect stdout and stderr to a log file
0 2 * * * /home/user/backup.sh >> /var/log/backup.log 2>&1
# Or redirect to /dev/null if you don't need output
0 2 * * * /home/user/backup.sh > /dev/null 2>&1
# For timestamped logs
0 2 * * * /home/user/backup.sh >> /var/log/backup.log 2>&1 && echo "$(date): Backup completed" >> /var/log/backup.log
5. Relative Path Problems
The Problem: Scripts use relative paths that work in your current directory but fail when cron runs from cron's home directory (usually /).
The broken script:
#!/bin/bash
# This works when you're in /home/user/project/
python3 main.py # Looks for ./main.py
cat data.txt # Looks for ./data.txt
The fix (Option A - cd first):
#!/bin/bash
cd /home/user/project || exit 1
python3 main.py
cat data.txt
The fix (Option B - Absolute paths):
#!/bin/bash
python3 /home/user/project/main.py
cat /home/user/project/data.txt
The fix (Option C - In crontab):
0 2 * * * cd /home/user/project && ./script.sh
6. Percent Signs in Crontab
The Problem: The percent sign % is a special character in crontab—it gets replaced with a newline. This breaks date formatting commands.
The broken cron entry:
# This will NOT work as expected
0 0 * * * tar -czf backup-$(date +%Y-%m-%d).tar.gz /home/user/data
The fix (escape the percent signs):
# Escape each % with \
0 0 * * * tar -czf backup-$(date +\%Y-\%m-\%d).tar.gz /home/user/data
Better fix (use a script):
# In crontab - call a script instead
0 0 * * * /home/user/backup-wrapper.sh
# In backup-wrapper.sh
#!/bin/bash
tar -czf "backup-$(date +%Y-%m-%d).tar.gz" /home/user/data
7. Permission and Ownership Issues
The Problem: Cron jobs run as the user who owns the crontab, but file permissions may prevent access to required resources.
Common symptoms:
- "Permission denied" errors in logs
- Script works when manually run with sudo, but fails in cron
- Cannot write to log files or output directories
The fix:
# Check what user the cron runs as
* * * * * whoami > /tmp/cron-user.txt
# Ensure script is executable
chmod +x /home/user/script.sh
# Ensure log directory is writable
touch /var/log/my-script.log
chmod 644 /var/log/my-script.log
# If you need root privileges, use root's crontab
sudo crontab -e # Edit root's crontab
# Or use sudo in the cron job (configure sudoers appropriately)
0 2 * * * sudo /home/user/root-required-script.sh
Debugging Workflow
When a cron job isn't working, follow this systematic approach:
- Check cron is running:
systemctl status cron # or crond on some systems ps aux | grep cron - Check cron logs:
grep CRON /var/log/syslog # Debian/Ubuntu grep CRON /var/log/cron # RHEL/CentOS journalctl -u cron -f # systemd systems - Test your command manually:
# Simulate cron's environment env -i SHELL=/bin/sh PATH=/usr/bin:/bin /bin/sh -c "your-command-here" - Add logging to your script:
#!/bin/bash exec >> /tmp/my-script-debug.log 2>&1 echo "Script started at $(date)" set -x # Enable command tracing # ... your script content ...
Quick Reference: Common Cron Debug Commands
| Command | Purpose |
|---|---|
crontab -l | List current user's cron jobs |
crontab -e | Edit current user's cron jobs |
grep CRON /var/log/syslog | Check cron execution logs |
systemctl status cron | Verify cron daemon is running |
env -i /bin/sh -c "cmd" | Test command with minimal environment |
Related Tools
- Cron Parser - Decode any cron expression and see next run times