Cron job fails with "command not found" — the PATH trap
You test a script in your shell, it works perfectly, you add it to crontab — and it silently never does its job. If you dig into the logs you find something like backup.sh: line 4: aws: command not found or pg_dump: command not found. The script was fine; cron's environment is not your environment.
Why it happens
Cron runs jobs with a deliberately minimal environment. In particular PATH is usually just /usr/bin:/bin — not the rich PATH your login shell builds from .bashrc, .profile, nvm, pyenv, Homebrew, or /usr/local/bin. So binaries that live outside /usr/bin and /bin — aws, docker, node, python3 from a venv, anything under /usr/local/bin — simply aren't found. Same reason your HOME-relative configs and shell functions aren't loaded.
The fixes
- Use absolute paths to every binary: /usr/local/bin/aws, /usr/bin/pg_dump. Find them with which aws in your interactive shell.
- Set PATH at the top of the crontab so all jobs inherit it:
- Source your profile inside the job if it needs your shell setup: bash -lc '/home/deploy/backup.sh' runs it as a login shell so .bash_profile loads.
The real danger: it fails silently
The "command not found" line lands in cron's mail spool or syslog — somewhere nobody watches. The job exits non-zero, does no work, and you find out days later that no backup has run. Exit codes and log-scraping don't help here because you're not reading those logs. What you need is to be told when the job stops succeeding.
Know the moment it breaks
Put a heartbeat ping at the very end of the job, chained so it only fires on success:
If a command not found aborts the script, the && short-circuits, the ping never fires, and Cronping emails you that the job went quiet — the first time it skips, not a week later. Add a /fail ping to go DOWN the instant a step errors:
Cronping does this free: 20 checks, 1-minute resolution, email + Slack/Discord alerts, no credit card. Create a check and get a ping URL in about ten seconds.
Get a ping URL in about ten seconds — no account, no email needed. Add an email later for alerts.
Not sure what it looks like? Watch a 15-second live demo of a check tripping and the alert landing.