"It works when I run it by hand but cron won't" is one of the most common Linux head-scratchers, and almost always for the same short list of reasons. Cron runs your job in a deliberately minimal environment — that difference is usually the culprit.
1. Confirm cron even tried
grep CRON /var/log/syslog # Debian/Ubuntu journalctl -u cron -e # or -u crond on RHEL crontab -l # is the job even installed for this user?
If there's no log line at the scheduled time, the schedule is wrong or the job is in a different user's crontab than you think. If cron ran it but it failed, read on.
2. PATH and environment — the #1 cause
Cron runs with a bare PATH (often just /usr/bin:/bin) and none of your shell's profile. A script that calls docker or aws finds them on your interactive PATH but not cron's. Fix it by using absolute paths to every binary, or setting PATH= at the top of the crontab.
3. Capture the output you're missing
Cron mails output nobody reads, so failures vanish. Redirect it to a log and the error becomes obvious:
# in the crontab, append to both stdout and stderr: 0 2 * * * /opt/backup.sh >> /var/log/backup.log 2>&1
4. Permissions, syntax, and the % trap
Make sure the script is executable (chmod +x) and owned by the right user. Check the five time fields are valid. And remember cron treats an unescaped % as a newline — escape it as \% (this bites date format strings constantly). Edit with crontab -e so the daemon reloads the file automatically.
Practise automation incidents
Failed cron/backup jobs are incidents in the simulator. Work them with Linux troubleshooting practice, or script resilient scheduled tasks with Ansible.