Linux and Bash for Developers · Environment, Networking and Remote Access
It Works in My Terminal but Not in cron
Reproduce cron's minimal environment, and make scheduled scripts independent of your interactive shell setup.
Scenario
A backup script works every time you run it by hand. Scheduled in cron, it fails every night with command not found or uploads to the wrong place, and nothing in the script changed.
What you see in production
command not foundfor tools that are definitely installed.- Relative paths resolving somewhere unexpected.
- Variables from your
~/.bashrc(API endpoints, credentials) are empty.
The naive code
#!/bin/bash
cd backups
mytool export --target "$BACKUP_BUCKET" # mytool lives in ~/bin, BACKUP_BUCKET comes from ~/.bashrc
Reproduce it
env -i starts a command with an (almost) empty environment, much like cron:
mkdir -p "$HOME/bin" backups
printf '#!/usr/bin/env bash\necho "exporting to ${1:-?}"\n' > "$HOME/bin/mytool"
chmod +x "$HOME/bin/mytool"
export PATH="$HOME/bin:$PATH" BACKUP_BUCKET="s3://acme-backups"
cat > backup.sh <<'EOF'
#!/bin/bash
cd backups && mytool "$BACKUP_BUCKET"
EOF
chmod +x backup.sh
echo "interactive: $(./backup.sh)"
echo "cron-like: $(env -i PATH=/usr/bin:/bin HOME="$HOME" ./backup.sh 2>&1 | sed 's/^.*: line [0-9]*: //')"
Output
interactive: exporting to s3://acme-backups cron-like: mytool: command not found
Run this on your computer — see “Get Started” for setup.
Root cause
cron runs jobs with a minimal environment: a short PATH (often /usr/bin:/bin), no ~/.bashrc or profile, /bin/sh as the default shell for the crontab line, and the home directory as the working directory. Anything the script relied on from your interactive session is missing.
The fix
Make the script self-contained: absolute paths, its own working directory, explicit configuration, strict mode, and logging:
mkdir -p "$HOME/bin" "$HOME/app/backups"
printf '#!/usr/bin/env bash\necho "exporting to ${1:?missing bucket}"\n' > "$HOME/bin/mytool"
chmod +x "$HOME/bin/mytool"
echo 'BACKUP_BUCKET=s3://acme-backups' > "$HOME/app/backup.env"
cat > "$HOME/app/backup.sh" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
export PATH="$HOME/bin:/usr/local/bin:/usr/bin:/bin"
cd "$(dirname "$(realpath "$0")")" # always run relative to the script
source ./backup.env # explicit configuration
cd backups
mytool "$BACKUP_BUCKET"
EOF
chmod +x "$HOME/app/backup.sh"
env -i HOME="$HOME" PATH=/usr/bin:/bin "$HOME/app/backup.sh"
echo '# crontab: 0 2 * * * $HOME/app/backup.sh >> $HOME/app/backup.log 2>&1'
Output
exporting to s3://acme-backups # crontab: 0 2 * * * $HOME/app/backup.sh >> $HOME/app/backup.log 2>&1
Run this on your computer — see “Get Started” for setup.
- Set
PATHand other required variables inside the script (or at the top of the crontab). cdto a known directory; never rely on the working directory.- Redirect output to a log and alert on failures; cron's email often goes nowhere.
- Test with
env -ibefore scheduling.
Trade-offs
- Sourcing an env file keeps secrets out of crontabs but must be protected with file permissions (
600). - systemd timers give better logs (
journalctl) and environment control (Environment=,EnvironmentFile=) than cron. - Hard-coding paths makes scripts less portable; derive them from the script's location instead.
Detect it in production
- Log every scheduled run with a timestamp and exit code; alert when a job hasn't succeeded on schedule.
- Heartbeat monitoring (the job pings a URL on success) catches jobs that silently stop running.
- Run
env -itests in CI for scheduled scripts.
Key points
- cron has a minimal environment: short PATH, no profile, different working directory.
- Make scheduled scripts self-contained and test them with env -i.
- Log and monitor scheduled jobs, or use systemd timers.
Further reading: crontab(5) · systemd.timer · Bash tutorial: scheduling
Practice questions
Why does a command found in your terminal fail in cron with 'command not found'?
Answer
cron uses a minimal PATH and doesn't load your shell profile, so directories like ~/bin or /usr/local/bin may be missing.
How can you test a script with an environment similar to cron's?
Answer
Run it with env -i and only the variables cron provides, for example env -i HOME=$HOME PATH=/usr/bin:/bin ./script.sh.
What advantages do systemd timers have over cron?
Answer
Logs in the journal, explicit environment settings, dependencies, resource limits, and catching up missed runs with Persistent=true.
Open in the app to mark lessons complete, take notes and track your progress.