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:

The fixes

Set an explicit PATH at the top of the crontab, use absolute paths everywhere, and source your environment inside the script:

PATH=/usr/local/bin:/usr/bin:/bin 0 3 * * * cd /srv/app && /usr/local/bin/python3 backup.py

Inside the script, load what an interactive shell would have given you:

#!/bin/bash set -euo pipefail source /home/me/.nvm/nvm.sh # or pyenv/rbenv init set -a; source /srv/app/.env; set +a # export the env file cd /srv/app node worker.js

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:

0 3 * * * cd /srv/app && ./backup.sh && curl -fsS https://cronping.cronping-oren.workers.dev/ping/<your-check-id>

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:

0 3 * * * cd /srv/app && (./backup.sh && curl -fsS https://cronping.cronping-oren.workers.dev/ping/<id> || curl -fsS https://cronping.cronping-oren.workers.dev/ping/<id>/fail)

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.

Try it now

Get a ping URL in about ten seconds — no account, no email needed. Add an email later for alerts.

← All guides