Quick fix
# One-liner: declare PATH at the top of the crontab, or use absolute paths in the job
# PATH=~/.nvm/versions/node/v20/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Put the PATH line as the first line of the crontab.
What you will run into
A crontab scheduled job reports command not found / wrong environment once it runs:
# The job calls node (installed via nvm), conda, or a custom script; the log has one line:
/bin/sh: 1: demo-node: not found
Signs: the command clearly works in an interactive terminal (which node returns something), but cron execution says command not found; or the job runs but can't read env vars (NVM_DIR, CONDA_PREFIX, custom exports);
the cron log only has one line /bin/sh: N: xxx: not found with no more clues.
Common with: tools installed via nvm/conda/pyenv, vars exported in ~/.bashrc, environments that only exist in login shells.
Why this happens
The shell cron uses to run jobs has a completely different environment from your interactive terminal:
- cron runs commands with
/bin/sh, which doesn't load~/.bashrc,~/.profile, or/etc/profile— nvm/conda/pyenv initialization (all written in~/.bashrc) is entirely missing; - The job's PATH is decided by the cron daemon: traditional SysV cron defaults to
/usr/bin:/bin, not including/usr/local/bin, let alone nvm/conda custom directories; - Counter-intuitive measurement (this machine, Ubuntu 26.04): when systemd starts
cron -f -P, jobs inherit systemd's PATH (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/snap/bin) — so/usr/local/binis actually available; - The real victims are paths and variables that are "only added in .bashrc", like
~/.nvm/versions/.../binand conda environment directories; - Aliases: also not effective in the cron environment.
Deterministic takeaway: "command not found / env var empty" in cron does not mean the system is broken. It means that command is not on cron's PATH.
Three correct paths:
- Export PATH explicitly at the top of the script, or use absolute command paths (
/usr/bin/python3). - Put the needed initializations inside the script itself.
- Run
bash -lcin the cron line to emulate a login environment — closest to a terminal, but brings terminal env uncertainty.
To debug, log the job with * * * * * cmd >> /tmp/cron.log 2>&1 and inspect what the job actually sees.
Fixed diagnostic: it works manually but fails under cron = an environment difference, not a script logic problem.
Step by step
1. Confirm the job's environment first — Required
Temporarily put printenv > /tmp/cron-env.txt 2>&1 into the job and run once to see PATH and vars - much faster than guessing; or read the cron daemon environment directly: tr '\0' '\n' < /proc/$(pgrep -x cron)/environ | grep PATH.
2. Fix A: declare PATH at the top of the crontab — Required
PATH=... as the crontab's first line lets the job use relative command names.
3. Fix B: use absolute paths in the job — Required
~/.nvm/versions/node/v20/bin/node /path/script.js
Doesn't depend on PATH; most robust.
4. Fix C: load the environment inside the job — Required
. "$HOME/.bashrc" && mycmd
Note .bashrc usually has non-interactive guards; more robust is writing what you need into a standalone env file and doing . /path/env.sh && mycmd in the job.
5. Debugging tip — Required
Redirect job output to a log (>> /tmp/job.log 2>&1); on failure the xxx in the first line is the command whose path/vars you need to add.
How to confirm it works
The job runs normally (measured output shape):
After declaring PATH in the crontab -> job log outputs NODE-OK <time> (measured)
Absolute-path version -> also runs normally (measured)Other environments
| Environment | Notes |
|---|---|
| Traditional SysV cron | Default PATH=/usr/bin:/bin; declare PATH at the top |
| systemd cron | PATH includes /usr/local/bin (measured); nvm/conda directories still need explicit declaration |
| nvm/conda/pyenv | Initialization is in .bashrc; cron doesn't load it |
| alias | Doesn't work in cron |
| Debugging | printenv into the job, or /proc/<cron pid>/environ |
Symptom
A crontab scheduled job reports command not found / wrong environment:
# The job calls node (nvm-installed), conda, or a custom script; the log has one line:
/bin/sh: 1: demo-node: not found
Signs: the command works in an interactive terminal (which node has a result), but cron execution says command not found; or the job runs but can't read env vars (NVM_DIR, CONDA_PREFIX, custom exports); the cron log only has one line /bin/sh: N: xxx: not found; common with nvm/conda/pyenv-installed tools, vars exported in ~/.bashrc, environments needing a login shell.
Cause
The shell cron uses to run jobs has a completely different environment from your interactive terminal:
- cron runs commands with
/bin/sh, not loading~/.bashrc,~/.profile,/etc/profile- nvm/conda/pyenv initialization (all in~/.bashrc) is missing; - The job's PATH is decided by the cron daemon: traditional SysV cron defaults to
/usr/bin:/bin, not including/usr/local/bin, let alone nvm/conda custom directories; - Counter-intuitive measurement (this machine, Ubuntu 26.04): with systemd starting
cron -f -P, jobs inherit systemd's PATH (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/snap/bin) -/usr/local/binis actually usable; the real victims are paths/vars like~/.nvm/versions/.../binand conda environment directories that are "only added in .bashrc"; - Aliases: also not effective in the cron environment.
Solution
- Confirm the job's environment first: temporarily put
printenv > /tmp/cron-env.txt 2>&1into the job and run once to see PATH and vars - faster than guessing; or read the cron daemon environment directly:tr '\0' '\n' < /proc/$(pgrep -x cron)/environ | grep PATH. - Fix A: declare PATH at the top of the crontab (most common):
PATH=~/.nvm/versions/node/v20/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binas the crontab's first line; the job can then use relative command names. - Fix B: use absolute paths in the job:
~/.nvm/versions/node/v20/bin/node /path/script.js- doesn't depend on PATH, most robust. - Fix C: load the environment inside the job:
* * * * * . "$HOME/.bashrc" && mycmd(note .bashrc usually has non-interactive guards; more robust is writing what you need into a standalone env file and doing. /path/env.sh && mycmdin the job). - Debugging tip: redirect cron job output to a log (
>> /tmp/job.log 2>&1); on failure thexxxin the first line/bin/sh: N: xxx: not foundis the command whose path/vars you need to add; after fixing, run again and confirm normal output in the log.
Verification Evidence
- Local measurement (2026-08-29, Ubuntu 26.04 LTS, systemd cron, job output redirected and captured):
- Environment baseline:
cron -f -P(systemd cron.service) -> cron process PATH measured as/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/snap/bin; - nvm/conda simulation: the command
demo-nodeonly installed in~/nvmbin/(not in the PATH above); - Failing version: crontab without declared PATH, job
* * * * * demo-node >> /tmp/cron-debug.log 2>&1-> after the next tick the log reads/bin/sh: 1: demo-node: not found(same phenomenon as SO #21775749); - Fix A:
PATH=$HOME/nvmbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binat the top of the crontab -> job runs normally, log outputsNODE-OK <time>; - Fix B: job changed to the absolute path
~/nvmbin/demo-node-> also runs normally; - Cleanup: test crontab removed, cron stopped, temp files deleted (no leftovers in the host root crontab).
- Source: SO #21775749 "Command not found even crontab PATH is set" (old tutorials generally only say "cron default PATH=/usr/bin:/bin"; this entry adds the measured correction for the systemd scenario).
Evidence details kept locally (2026-08-29)
Applicable tools (1)
This problem is linked from the tool pages below — reachable from either side.
cronSource & evidence
Source: https://stackoverflow.com/questions/21775749/command-not-found-even-crontab-path-is-set
← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk