Cron Job Not Running? Check the Environment First

Linux & Unix Intermediate 👁 9 views 📅 Jul 4, 2026

Cron jobs fail silently because they miss your shell environment. Here is the exact fix and why it works.

You set up a cron job, checked the syntax, waited for the time to pass, and nothing happened. No output, no error, no email. The job refuses to run. I've been there, and the reason is almost always the same: cron doesn't load your shell environment.

The Quick Fix

Edit your crontab with crontab -e. At the top, add these two lines:

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin

Then, in your cron command itself, use the full path to any script or executable. For example:

30 2 * * * /home/you/scripts/backup.sh

Don't rely on backup.sh being found. Give the absolute path. That's it. Restart cron with service cron restart or systemctl restart cron depending on your distro.

I see this fail on Ubuntu 22.04, Debian 11, and CentOS 7 all the time. The default cron PATH is often just /usr/bin:/bin. If your script calls anything in /usr/local/bin — like python3 or node — cron won't find it.

Why This Works

Cron runs with an extremely minimal environment. It sets only a few variables: HOME (your home dir), LOGNAME (your username), SHELL (defaults to /bin/sh), and PATH (often just /usr/bin:/bin on older systems, /usr/bin:/bin:/usr/sbin:/sbin on newer ones).

What cron does not do is source your .bashrc, .profile, or .bash_profile. Those files set aliases, additional PATH entries, and other custom variables. When cron runs your command, it doesn't see any of that. The result is a job that runs but immediately fails because python3 or docker or aws isn't found.

By explicitly setting SHELL and PATH at the top of the crontab, you tell cron exactly where to find everything. The SHELL=/bin/bash line makes cron use bash instead of sh, which gives you more consistent behavior with things like source or [[ ]] test syntax. The PATH line overrides the default search paths.

Also, specifying the full path to your script avoids any ambiguity. If the script itself has a shebang line like #!/usr/bin/env bash, that's fine — cron will use whatever interpreter the shebang says, but it still needs to find the script file first.

Less Common Variations of This Issue

1. The cron job runs but produces no output

Sometimes the job runs but you never see standard output or errors. By default, cron tries to email the output to the crontab's owner. If mail isn't set up (common on headless servers), the output disappears. The fix: redirect output to a log file.

30 2 * * * /home/you/scripts/backup.sh >> /home/you/logs/backup.log 2>&1

The >> appends stdout. The 2>&1 sends stderr to the same place. Check that file after the next cron run. If it's empty, the job didn't execute at all (go back to the PATH fix). If it has output, you can see what's wrong.

2. Environment variables like HOME are wrong

On some systems, cron sets HOME to /root even for non-root users. This breaks scripts that assume $HOME points to /home/you. The fix: either hardcode paths or set HOME explicitly in the crontab.

HOME=/home/you
30 2 * * * /home/you/scripts/backup.sh

3. The job runs but a specific command fails inside the script

Your script works from the terminal but fails in cron. The interior commands might depend on environment variables you set in .bashrc, like JAVA_HOME or GOPATH. Cron won't have those. Inside your script, export them explicitly at the top.

#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
# rest of script

This makes the script self-contained. No need to source anything from .bashrc.

4. Using @reboot and it fails

The @reboot special string runs the job when cron starts. But if the system boots before some services or mounts are ready, your script might fail. The fix: add a short sleep at the start of the script.

#!/bin/bash
sleep 30
# rest of script

30 seconds is usually enough for most services to start. Adjust based on your system.

5. Cron permissions are wrong

If your script is owned by root but you're running it from a user's crontab, cron might refuse to execute it. The script must be readable and executable by the user who owns the crontab. Check with ls -l /path/to/script. If permissions are off, fix them:

chmod +x /home/you/scripts/backup.sh
chown you:you /home/you/scripts/backup.sh

Also, the crontab file itself must be owned by the user running it. A common mistake is editing the root crontab with sudo crontab -e but then expecting it to run as a regular user. Use crontab -e without sudo for your own user.

Prevention

Before you write a new cron job, do three things:

  1. Test the command manually from a minimal shell. Open a terminal and run env -i HOME=$HOME PATH=/usr/bin:/bin /bin/sh -c 'your_command'. This simulates the cron environment. If it fails, you know the fix.
  2. Use absolute paths everywhere. In the crontab command and inside any scripts. Avoid ./relative/path or relying on which.
  3. Log everything. Append >> /some/log/file 2>&1 to every cron job. You can always delete old logs, but you can't debug a silent failure.

If you follow these rules, cron stops being a black box. It becomes a predictable tool that runs your stuff exactly when you tell it to.

Was this solution helpful?