Cron troubleshooting
Job never runs
Confirm the cron daemon is running: systemctl status cron (or crond).
List the crontab with crontab -l and verify field syntax.
Check grep CRON /var/log/syslog or /var/log/cron for execution entries.
Command not found
Cron uses a minimal PATH — often just /usr/bin:/bin.
Use absolute paths for every command, or set PATH= at the top of the crontab.
Script works manually but fails in cron
Run as the cron user: sudo -u <user> /path/to/script.sh.
Cron does not load .bashrc — environment variables, aliases, and
cd assumptions from your shell session won't apply.
No error output visible
Redirect stdout and stderr to a log file:
/path/script.sh >> /var/log/job.log 2>&1.
Set MAILTO=you@example.com to receive cron mail, or check
/var/mail/<user> on the server.
Permission denied
The script must be executable (chmod +x) and readable by the cron user.
Log files and output directories need write permission for that user too.
Overlapping or duplicate runs
Long-running jobs can stack if the schedule is shorter than runtime.
Use flock -n /var/lock/myjob.lock /path/to/script.sh to skip if already running.
Percent sign (%) in crontab
In crontab lines, % starts a newline in the command field.
Escape it or avoid it in date format strings:
# Wrong — breaks the line
0 * * * * echo "time: $(date +%Y-%m-%d)" >> /tmp/out
# Escape % as \%
0 * * * * echo "time: $(date +\%Y-\%m-\%d)" >> /tmp/outDay-of-month vs day-of-week
When both day-of-month and day-of-week are set (not *),
the job runs when either condition matches (OR logic), not both.
# Runs on the 1st AND every Monday (not just Mon the 1st)
0 9 1 * 1 /path/script.shDebugging workflow
1. Verify syntax and list crontab
crontab -l
# paste expression into https://crontab.guru for a sanity check2. Run the exact command as the cron user
sudo -u deploy bash -lc '/usr/local/bin/backup.sh >> /var/log/backup.log 2>&1'3. Watch cron logs in real time
tail -f /var/log/syslog | grep CRON # Debian/Ubuntu
tail -f /var/log/cron # RHEL/CentOSLogging pattern (recommended)
Wrap jobs so every run leaves a trace:
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
# Or with timestamps in the script:
log() { echo "[$(date -Iseconds)] $*" >> /var/log/backup.log; }
log "Starting backup"
/usr/bin/rsync ... || log "ERROR: rsync failed with $?"Practice scenarios
Hands-on Cron scenarios on live Linux VMs: cron