Back to Blog
Troubleshooting10 min read

Cron Job Not Running? 12 Common Causes & Fixes

You set up a cron job. It does not run. There is no error message, no log entry, nothing. This is one of the most frustrating debugging experiences in system administration because cron fails silently by default. This guide walks through every common cause, in order of likelihood, with the exact commands to diagnose and fix each one.

Quick Diagnostic Checklist

Run these three commands first. They catch 80% of cron issues:

# 1. Is the cron daemon running?
systemctl status cron     # Debian/Ubuntu
systemctl status crond    # CentOS/RHEL

# 2. What's in your crontab?
crontab -l

# 3. What do the cron logs say?
grep CRON /var/log/syslog | tail -20          # Debian/Ubuntu
grep CRON /var/log/cron | tail -20            # CentOS/RHEL
journalctl -u cron --since "1 hour ago"       # systemd
1

PATH Is Not Set

This is the number one cause of cron jobs that "work when I run them manually but not in cron." When you run a command in your terminal, your shell has a rich PATH variable that includes directories like /usr/local/bin, /home/user/.nvm/bin, etc. Cron uses a minimal PATH, typically just /usr/bin:/bin.

Fix: Use absolute paths for all commands, or set PATH explicitly at the top of your crontab:

# Set PATH at the top of crontab
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Or use absolute paths in each job
0 2 * * * /usr/bin/python3 /home/deploy/scripts/backup.py
0 * * * * /usr/local/bin/node /home/deploy/app/cleanup.js

To find the absolute path of any command, run which python3 or which node in your terminal.

2

Permission Denied on the Script

Your script needs execute permission, or the cron job needs to invoke the interpreter explicitly. A common scenario: you created a script and forgot chmod +x.

Fix:

# Option A: Make the script executable
chmod +x /home/deploy/scripts/backup.sh

# Option B: Call the interpreter explicitly (no chmod needed)
0 2 * * * /bin/bash /home/deploy/scripts/backup.sh
0 3 * * * /usr/bin/python3 /home/deploy/scripts/report.py

Also check file ownership. If the crontab belongs to user deploy but the script is owned by root with mode 700, the cron job will fail silently.

3

Syntax Error in the Cron Expression

A subtle syntax error in the cron expression can make the job never match. Common mistakes include using 6 fields instead of 5, confusing day-of-month and day-of-week positions, or using unsupported characters.

Fix: Validate your expression with our Cron Expression Generator to preview the next execution times before committing it to your crontab.

# WRONG: 6 fields (seconds field is not supported by system cron)
*/30 * * * * * /usr/bin/php /var/www/task.php

# CORRECT: 5 fields
*/30 * * * * /usr/bin/php /var/www/task.php

# WRONG: L (last) and W (weekday) are NOT supported in Vixie cron
0 0 L * * /usr/bin/php /var/www/monthly.php

# CORRECT: Use a workaround or check in the script
0 0 28-31 * * [ "$(date +%d -d tomorrow)" == "01" ] && /usr/bin/php /var/www/monthly.php
4

Environment Variables Are Missing

Cron runs jobs in a stripped-down environment. Variables you set in .bashrc, .profile, or .env files are not available. Your script tries to read DATABASE_URL, gets nothing, and fails.

Fix: Define variables in the crontab itself, source them in the command, or use a wrapper script:

# Option A: Define at the top of crontab
DATABASE_URL=postgresql://user:pass@localhost/mydb
API_KEY=sk-abc123

0 * * * * /usr/bin/python3 /home/deploy/sync.py

# Option B: Source a .env file in the command
0 * * * * . /home/deploy/.env && /usr/bin/python3 /home/deploy/sync.py

# Option C: Use env in a wrapper script
#!/bin/bash
source /home/deploy/.env
cd /home/deploy
python3 sync.py

To see exactly what environment cron provides, add this temporary job:

* * * * * env > /tmp/cron_env.txt
5

Wrong User's Crontab

Every user has their own crontab. If you added a job to root's crontab but the script needs to run as www-data, or vice versa, the job either runs with wrong permissions or accesses the wrong files.

Fix: Verify which user's crontab you are editing:

# Check current user
whoami

# List current user's crontab
crontab -l

# List a specific user's crontab (requires root)
crontab -u www-data -l

# Edit a specific user's crontab
sudo crontab -u www-data -e

# Also check the system crontab (different format with user field)
cat /etc/crontab

Note: /etc/crontab has a sixth field specifying the user. Per-user crontabs (crontab -e) do not. Mixing up these formats is a common source of errors.

6

Timezone Confusion

Your cron expression says 0 9 * * * (9 AM). But 9 AM in which timezone? System cron uses the server's timezone. If your server is in UTC and you expected local time, your job runs hours earlier or later than intended.

Fix: Check the server's timezone and either adjust your schedule or change the timezone:

# Check current timezone
timedatectl
# or
cat /etc/timezone

# Set timezone for cron (add to top of crontab)
CRON_TZ=America/New_York
0 9 * * * /home/deploy/scripts/morning-report.sh

# Or convert manually: 9 AM Eastern = 14:00 UTC (winter)
0 14 * * * /home/deploy/scripts/morning-report.sh

The CRON_TZ variable is supported by some cron implementations (like Vixie cron on many Linux distributions) but not all. If your system does not support it, convert to UTC manually or use a scheduling service with per-job timezone support.

Tired of debugging silent cron failures?

CronJobPro logs every execution with status codes, response times, and output. When a job fails, you get notified instantly via email or webhook. No more guessing.

Try CronJobPro Free
7

Mail System Overflow

By default, cron tries to email the output of every job to the local user. If the local mail system is broken or the mailbox is full, cron may stop running new jobs. On some systems, a full /var/spool/mail/ can also fill the disk partition.

Fix: Redirect output explicitly and suppress cron mail:

# Redirect stdout and stderr to a log file
0 2 * * * /home/deploy/backup.sh >> /var/log/backup.log 2>&1

# Or suppress output entirely
0 2 * * * /home/deploy/backup.sh > /dev/null 2>&1

# Disable mail for all jobs (add to top of crontab)
MAILTO=""
8

Overlapping Job Runs

If a job takes longer than the schedule interval, multiple instances run simultaneously. This can cause database locks, corrupted files, or resource exhaustion. The new instance might also fail because the previous one holds a lock, making it look like the job "doesn't work."

Fix: Use flock to prevent overlapping runs:

# Use flock to skip the job if the previous run is still active
*/5 * * * * /usr/bin/flock -n /tmp/sync.lock /home/deploy/sync.sh

# -n = non-blocking (skip if locked)
# The lock file is automatically released when the script exits
9

Disk Full

When the disk is full, cron cannot write to its spool directory, temporary files, or log files. Some cron implementations stop scheduling entirely when there is no disk space. Your script might also fail because it cannot write output files or temp data.

Fix:

# Check disk usage
df -h

# Find large files eating space
du -sh /var/log/* | sort -rh | head -10

# Check if log rotation is working
ls -la /var/log/*.gz

# Common disk hogs:
# - /var/log/ — unrotated logs
# - /var/spool/mail/ — cron output mail
# - /tmp/ — leftover temp files
# - Docker: /var/lib/docker/
10

The Cron Daemon Is Not Running

On some systems, especially minimal Docker containers, VPS instances, or after an OS upgrade, the cron service might be stopped or not installed at all.

Fix:

# Check if cron is running
systemctl status cron       # Debian/Ubuntu
systemctl status crond      # CentOS/RHEL

# Start it
sudo systemctl start cron
sudo systemctl enable cron  # Start on boot

# On Alpine Linux (no systemd)
rc-service crond status
rc-service crond start
11

Missing Newline at End of Crontab

This one catches people constantly. The cron specification requires that each crontab file ends with a newline character. If the last line of your crontab does not have a trailing newline, that last job will be silently ignored.

Fix: Always ensure your crontab ends with a blank line:

0 2 * * * /home/deploy/backup.sh
0 * * * * /home/deploy/sync.sh
# This blank line below is required:
12

Special Characters Not Escaped

The percent sign (%) has special meaning in crontab. It is treated as a newline character, and everything after the first % is sent as stdin to the command. This commonly breaks date commands.

Fix: Escape percent signs with a backslash:

# WRONG: % will break this
0 2 * * * /usr/bin/pg_dump mydb > /backups/db_$(date +%Y%m%d).sql

# CORRECT: Escape the % characters
0 2 * * * /usr/bin/pg_dump mydb > /backups/db_$(date +%Y%m%d).sql

# ALTERNATIVE: Put the command in a script (no escaping needed)
0 2 * * * /home/deploy/scripts/backup.sh

Step-by-Step Debugging Procedure

If you have gone through all 12 causes and your job still does not run, follow this systematic procedure:

  1. 1Add a trivial test job that writes a timestamp to a file every minute. If this does not work, the problem is with cron itself, not your script.* * * * * echo "cron works $(date)" >> /tmp/cron-test.txt
  2. 2Run your exact command manually as the same user cron will use. Switch users with sudo -u deploy bash and then paste the command from your crontab.
  3. 3Run with cron's environment. Create a script that sources the minimal cron environment and then runs your command. Compare behavior with your interactive shell.
  4. 4Capture all output. Add >> /tmp/job.log 2>&1 to your crontab entry and inspect the log after the next scheduled run.
  5. 5Check cron logs for any rejection messages using the commands from the diagnostic checklist above.

Avoid These Problems Entirely

Most of these 12 issues exist because system cron was designed in 1979 for a different era. It has no logging, no monitoring, no alerting, and no web interface. Every failure is silent by default.

If your cron job calls an HTTP endpoint (which is the case for most web application tasks), an external scheduling service eliminates these problems by design:

ProblemSystem CronCronJobPro
PATH issuesCommon, manual fixN/A (HTTP request)
Silent failuresDefault behaviorEmail/webhook alert
No execution logMust configure manuallyFull history dashboard
Timezone confusionServer timezonePer-job timezone setting
Server goes downJobs stopStill attempts, alerts you

Related Articles

Never debug a silent cron failure again

CronJobPro monitors every execution and alerts you instantly when something fails. Full logs, response codes, and timing data for every run. Free for up to 5 jobs.