Linux  ·  low  ·  Day-to-day operations

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

cron not runningcrontabcommand not found cronno MTA installed

The fix

Give the job an environment and somewhere to complain
Shell25 minuteslow riskreversible

Any cron job that works interactively and not on schedule.

  1. Prove cron is even running the job.

    Shell
    sudo systemctl status cron 2>/dev/null || sudo systemctl status crondsudo journalctl -u cron -u crond --since '2 hours ago' --no-pager | tail -30
  2. Capture the output — by far the most useful single change. Without this the job fails into nothing.

    Shell
    crontab -l
  3. Redirect 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.

  4. See exactly what environment the job gets by scheduling a one-off dump of it.

    Shell
    ( crontab -l; echo '* * * * * env > /tmp/cron-env.txt 2>&1' ) | crontab -sleep 70 && cat /tmp/cron-env.txt
  5. Use absolute paths for every binary, or set PATH at the top of the crontab.

    Shell
    ( echo 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'; crontab -l | grep -v '^PATH=' ) | crontab -
  6. Escape any percent signs — cron treats an unescaped % as a newline, which silently truncates the command.

    Shell
    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.

  7. For anything non-trivial, prefer a systemd timer — it logs to the journal, has proper dependencies, and can be tested on demand.

    Shell
    systemctl list-timers --all | head
Confirm it workedThe job runs on schedule and the log file shows its output.
Shell
tail -20 /var/log/job.log
If you need to undo itcrontab -e to restore the previous entry; keep a copy with crontab -l > ~/crontab.bak first.

Where 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.