A cron job works by hand but not on schedule
cron runs jobs with a minimal environment — a short PATH, no profile, and a different shell — so a script that depends on an interactive login environment fails silently.
What you see
The script runs perfectly from your shell and does nothing on schedule, or fails with command not found. Often nothing at all is logged.
What is actually wrong
PATH is typically only /usr/bin:/bin under cron. No ~/.bashrc or ~/.profile is sourced. Percent signs in the command line have a special meaning. And nothing captures the output unless you ask it to.
Codes and articles
The fix
Give the job an environment and somewhere to complain
Any cron job that works interactively and not on schedule.
Prove cron is even running the job.
sudo systemctl status cron 2>/dev/null || sudo systemctl status crondsudo journalctl -u cron -u crond --since '2 hours ago' --no-pager | tail -30
Capture the output — by far the most useful single change. Without this the job fails into nothing.
crontab -lRedirect both streams to a log file in the crontab entry, e.g. `*/5 * * * * /opt/scripts/job.sh >> /var/log/job.log 2>&1`.
cron mails output to the local user by default, and on a machine with no MTA that mail goes nowhere at all. Redirecting is what makes the failure visible.
See exactly what environment the job gets by scheduling a one-off dump of it.
( crontab -l; echo '* * * * * env > /tmp/cron-env.txt 2>&1' ) | crontab -sleep 70 && cat /tmp/cron-env.txt
Use absolute paths for every binary, or set PATH at the top of the crontab.
( echo 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'; crontab -l | grep -v '^PATH=' ) | crontab -Escape any percent signs — cron treats an unescaped % as a newline, which silently truncates the command.
echo '0 2 * * * /usr/bin/mysqldump db > /backup/db-$(date +\%F).sql'This one catches almost everybody with a date-stamped filename. The command runs, just not the whole of it.
For anything non-trivial, prefer a systemd timer — it logs to the journal, has proper dependencies, and can be tested on demand.
systemctl list-timers --all | head
tail -20 /var/log/job.logWhere this stops. This write-up was written and checked by hand. It says what each step changes, how to confirm it worked and how to reverse it, and anything destructive is flagged before you reach it. If it does not match what your machine is doing, search the Support Centre for the exact code or message — and when something needs a person, get in touch.