Why does a script that runs smoothly in your terminal error with "command not found" in crontab? Because cron runs commands with the minimal shell /bin/sh and a very short PATH. This episode covers cron's restricted environment and its solution: SHELL=/bin/bash, a full PATH, and HOME.

In episode 5 we secured job output. Now we face one of the biggest mysteries in the world of cron: "why does it work in the terminal, but error in crontab?"
The answer is almost always the same: environment. When you log in and type a command, your terminal inherits the full environment from your login shell — a long PATH, HOME, LANG, and so on. When cron runs a command, it only provides a minimal environment with the shell /bin/sh. This difference is the source of countless bugs.
Cron runs commands via /bin/sh — not your interactive bash. On many distros, /bin/sh is just the simpler dash. Bash features like [[ ]], arrays, or ${VAR:-default} may not work.
Cron's default PATH is very short, generally only:
/usr/bin:/binCommands in /usr/local/bin, /sbin, or special locations won't be found. That's why you see command not found even though the same command works in the terminal.
Cron provides a few automatic variables — among them HOME, LOGNAME, and SHELL — but it does not inherit your interactive environment like EDITOR, aliases, or custom exports.
Test it directly: write a crontab that dumps the environment, then compare.
* * * * * env > /tmp/cron-env.txt 2>&1Compare it with your terminal's environment:
env > /tmp/shell-env.txt
diff /tmp/shell-env.txt /tmp/cron-env.txtThe differences in PATH and other variables will be immediately visible. This is the root of most "works in the terminal, fails in cron" cases.
Crontab supports defining variables at the top of the file — outside schedule lines:
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
HOME=/home/deploy
MAILTO=""
30 2 * * * /usr/local/bin/backup.shWith SHELL=/bin/bash, jobs run under full bash — arrays, [[ ]], and other bash operators work. A complete PATH ensures commands are found wherever they're installed.
An extra safety net: inside scripts, reference binaries by absolute path or build your own PATH:
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin
export HOME=/home/deploy
rsync -a /data /backup >> /var/log/backup.log 2>&1This way the script still works even if it's run from an environment poorer than the crontab's.
Note
There are two layers that often get mixed up: the crontab environment (variables at the top of the file) and the script environment (exports inside the script). The deeper the layer, the harder it is to trace when an error occurs. Establish one policy: always set a full PATH in the crontab and use absolute paths in scripts for critical things.
| Aspect | Interactive terminal | Cron |
|---|---|---|
| Shell | login bash | /bin/sh (dash) |
| PATH | Long, from profile | Short (/usr/bin:/bin) |
| Custom variables | Inherited | Not inherited |
| HOME | Correct | Sometimes wrong |
SHELL, PATH, and HOME at the top of the crontab..bashrc; cron doesn't read them.Tip
When a script errors with "command not found", ask two questions: is the binary in the PATH cron uses, and does the script run it with an absolute path? Both can be fixed in a minute — but they can frustrate you all day if you don't understand the root cause.
Key takeaways:
/bin/sh with a short PATH and minimal environment.SHELL=/bin/bash, a full PATH, and HOME at the top of the crontab.env > /tmp/cron-env.txt and compare with your terminal.In episode 7 we'll cover debugging and testing jobs — from manual command tests, cron -n for foreground, checking exit codes, sentinel files, to the crontab.guru validator and testing with an every-minute schedule. Time to turn frustration into method!