Cron job works manually but fails in crontab (the PATH/environment trap)
The most maddening cron bug: you run ./backup.sh in your shell and it works perfectly, but the identical line in your crontab does nothing. No crash, no obvious error - the job just quietly fails to do its work. The cause is almost always the environment. Cron does not run your job with your login shell's setup.
Why it happens
An interactive login sources /etc/profile, ~/.bashrc, ~/.profile and friends. Cron does none of that. It runs jobs with a bare, minimal environment - typically PATH=/usr/bin:/bin and little else. So anything that relies on your shell setup breaks under cron:
- A binary in /usr/local/bin, ~/bin, or a language version manager (nvm, pyenv, rbenv) - cron's PATH can't find it, so command not found.
- Relative paths - cron starts in the user's home dir, not where you were cd'd.
- Environment variables you set in .bashrc or a .env - unset under cron, so API keys and config are empty.
- % characters in the command - cron treats an unescaped % as a newline.
The fixes
Set an explicit PATH at the top of the crontab, use absolute paths everywhere, and source your environment inside the script:
Inside the script, load what an interactive shell would have given you:
The part everyone forgets: you won't be told
Here's the real danger. When a cron job errors, cron mails the output to the local user's mail spool - which on most modern servers nobody reads, or isn't even configured. So the job fails with command not found and you find out days later when the backups aren't there. Testing by hand can't catch this, because by hand you have the right environment.
The way to catch it is a dead-man's-switch: ping only after the real work finishes, so a broken-environment run that never gets there simply never pings - and you get an alert:
The && is the whole trick: if the script dies on a missing command or an empty variable, the ping is skipped, the window lapses, and Cronping emails you. Want the failure to page you instantly instead of waiting for the window? Add a fail ping:
Cronping does this free: 20 checks, 1-minute resolution, full history, email + Slack/Discord/webhook 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.