Linux Error Guide: 'cron job not running' — Fix Silent Cron Failures
Fix a cron job that never runs on Linux: check the daemon and schedule, read cron logs, fix PATH and environment, escape %, set MAILTO, and correct crontab and /etc/cron.d permissions.
- #linux
- #troubleshooting
- #errors
- #cron
Stuck on this Linux Admins error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
Cron rarely prints a loud error when a job fails to run — the job simply never fires, which makes it one of the most frustrating Linux problems to debug. The clues live in the system log, where a triggered job leaves a line like this:
Jul 5 09:00:01 web01 CRON[20481]: (deploy) CMD (/opt/scripts/backup.sh)
Jul 5 09:00:01 web01 CRON[20480]: (CRON) info (No MTA installed, discarding output)
If you never see a matching CMD line at the scheduled time, cron is not even attempting your job. If you see the CMD line but the work still does not happen, cron ran your command in a stripped-down environment where it failed silently. Both cases look identical from the outside — “the cron job is not running” — but have completely different fixes.
Symptoms
- A script that runs perfectly by hand does nothing when scheduled.
- No output, no log entry, and no error email arrive at the scheduled minute.
- The job works for one user but not another, or works interactively but not from
/etc/cron.d. - A command with
date +%Y-%m-%dor a%sign behaves strangely or truncates. systemctl status cron(orcrond) shows the service inactive or failed after a reboot.
Common Root Causes
- The cron daemon is stopped or disabled, so no schedules are evaluated at all.
- Wrong crontab scope — the job sits in your user crontab but you expected root’s, or vice versa.
- A minimal PATH — cron runs with
PATH=/usr/bin:/bin, so bare commands likeaws,docker, ornodeare not found. - Missing environment variables — cron does not source
~/.bashrcor~/.profile, so anything defined there is absent. - Unescaped
%characters, which cron interprets as newlines inside the command. - Incorrect permissions or format on
/etc/cron.dfiles (world-writable files and files with a wrong username field are ignored). - No MTA installed, so failure output is discarded instead of mailed to you.
Diagnostic Workflow
Confirm the daemon is actually running. The service name differs by distribution — cron on Debian/Ubuntu, crond on RHEL/Fedora:
systemctl status cron # Debian / Ubuntu
systemctl status crond # RHEL / Fedora / Alma
systemctl is-enabled cron # will it survive a reboot?
List the crontab for the exact user that should own the job. This is the single most common mistake — checking the wrong scope:
crontab -l # the current user's crontab
sudo crontab -l -u deploy # a specific user's crontab
sudo crontab -l -u root # root's crontab
Watch the cron log to see whether the job is even triggered. On systemd hosts use the journal; on older or Debian systems grep syslog:
journalctl -u cron --since "10 min ago" # Debian / Ubuntu
journalctl -u crond --since "10 min ago" # RHEL
grep CRON /var/log/syslog | tail -20 # Debian, if journal is unavailable
grep CROND /var/log/cron | tail -20 # RHEL
If you see the CMD line but the job still fails, the problem is the environment. Capture cron’s real environment by dumping it from inside a temporary cron entry, then compare it to your shell:
* * * * * env > /tmp/cron-env.txt 2>&1
diff <(sort /tmp/cron-env.txt) <(env | sort)
Check /etc/cron.d file permissions and format — cron ignores files that are group- or world-writable, and system crontabs there require a username field:
ls -l /etc/cron.d/
# system crontab format includes the user (the 6th field):
# 0 2 * * * deploy /opt/scripts/backup.sh
Example Root Cause Analysis
A nightly backup script ran fine as ./backup.sh but produced no archive when scheduled. journalctl -u cron showed the CMD line firing every night at 02:00, so cron was triggering the job — the environment was the culprit.
The script called aws s3 cp ... using a bare aws. Interactively, aws resolved because /usr/local/bin was on the user’s PATH via ~/.bashrc. Under cron, PATH was only /usr/bin:/bin, so aws: command not found — and because no MTA was installed, that error was discarded (No MTA installed, discarding output). The fix was to set an explicit PATH and MAILTO at the top of the crontab and to redirect output to a log file:
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=ops@example.com
0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
A second job that embedded $(date +\%Y\%m\%d) had failed for a different reason: the unescaped % was read as a newline, truncating the command. Escaping each % as \% restored it.
Prevention Best Practices
- Always set an explicit
PATHline at the top of the crontab, and use absolute paths for interpreters and binaries (/usr/bin/python3, notpython3). - Redirect every job’s output to a log file with
>> /var/log/job.log 2>&1so failures are recorded, not discarded. - Set
MAILTO=and install an MTA (or a null-mailer) so cron can deliver error output. - Escape literal
%characters as\%inside cron commands. - Keep
/etc/cron.dfiles owned by root, mode0644, and include the username field. - Test the exact scheduled command with
env -i /bin/sh -c '<command>'to mimic cron’s clean environment before committing it.
Quick Command Reference
systemctl status cron # daemon health (crond on RHEL)
crontab -l # current user's jobs
sudo crontab -l -u <user> # another user's jobs
journalctl -u cron --since today # what cron actually triggered
grep CRON /var/log/syslog # Debian syslog fallback
* * * * * env > /tmp/cron-env.txt # capture cron's environment
ls -l /etc/cron.d/ # verify system crontab perms
Conclusion
A cron job that “isn’t running” is almost always one of two things: cron is not triggering it (wrong scope, stopped daemon, bad /etc/cron.d permissions), or cron is triggering it in a minimal environment where it fails silently (missing PATH, unescaped %, discarded output). Reading the cron log tells you which half of the problem you have, and from there the fix is quick. Bake in explicit PATH, absolute paths, logging, and MAILTO from the start and your scheduled jobs stop being a mystery. For more Linux fixes, see the Linux admin guides.
Fixed it? Get 500 Linux Admins & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.