Common Issues

Cron Jobs Not Running

Diagnose and fix cron jobs that aren't executing, PATH issues, permissions, syntax errors, and silent failures.

Cron jobs failing silently is a common frustration. Here's a systematic approach to finding out why a job isn't running.

Step 1: Check Cron Service

# Debian/Ubuntu
systemctl status cron

# RHEL/CentOS/AlmaLinux
systemctl status crond

# Start if stopped
systemctl start cron
systemctl enable cron

Step 2: Check Cron Logs

# Ubuntu/Debian
grep CRON /var/log/syslog | tail -30

# With journald
journalctl -u cron --since "1 hour ago"
journalctl -u crond --since today

# RHEL/CentOS
grep cron /var/log/cron | tail -30

A successful run looks like:

cron[1234]: (root) CMD (/usr/local/bin/backup.sh)

If you see the job in logs but it's failing, the issue is in the script itself. If you don't see it at all, the issue is with cron configuration.

Common Causes and Fixes

1. PATH Is Too Restrictive

Cron runs with a minimal PATH (/usr/bin:/bin). Commands available in your shell may not be found.

# Bad - 'node', 'python3', 'composer' may not be in cron's PATH
* * * * * node /app/script.js

# Good - use full paths
* * * * * /usr/bin/node /app/script.js
* * * * * /usr/bin/python3 /app/script.py

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

Find the full path of a command:

which node
which python3
which composer

2. Script Not Executable

# Check permissions
ls -la /usr/local/bin/backup.sh

# Fix
chmod +x /usr/local/bin/backup.sh

# Also ensure the script has a proper shebang
head -1 /usr/local/bin/backup.sh
# Should be: #!/bin/bash or #!/bin/sh

3. Missing Newline at End of Crontab

POSIX requires a newline at the end of the crontab file. Without it, the last job is silently ignored.

# Always edit with:
crontab -e

# Never edit directly: /var/spool/cron/crontabs/root

4. Output Going Nowhere (Silent Failures)

By default, cron emails output. If no mail agent is configured, output is lost.

# Redirect output to a log file
* * * * * /usr/local/bin/backup.sh >> /var/log/backup-cron.log 2>&1

# Discard all output (not recommended for debugging)
* * * * * /usr/local/bin/backup.sh > /dev/null 2>&1

# Suppress email for the whole crontab
MAILTO=""

5. Wrong User's Crontab

# Check which user's crontab you're editing
crontab -l           # current user
crontab -l -u nginx  # specific user

# System-wide crontabs (include username field)
cat /etc/crontab
ls /etc/cron.d/

6. Environment Variables Not Available

Cron doesn't source .bashrc, .profile, or /etc/environment:

# Set variables directly in the crontab
RAILS_ENV=production
DB_HOST=localhost

* * * * * /usr/bin/ruby /app/task.rb

# Or source the environment in the script
#!/bin/bash
source /etc/environment
source /home/user/.bashrc
# ... rest of script

7. Crontab Syntax Error

# Validate your cron expression at https://crontab.guru

# Common mistakes:
# "every 5 minutes" → */5 * * * *  (not 5 * * * *)
# "at 3am daily"    → 0 3 * * *
# "every hour"      → 0 * * * *

8. Script Fails Due to Working Directory

Cron sets the working directory to $HOME. Relative paths in scripts fail:

# Bad
cd logs && ./process.sh

# Good
cd /var/www/myapp/logs && ./process.sh
# or
cd "$(dirname "$0")" && ./process.sh

9. User Has No Login Shell

# Check the user's shell
grep www-data /etc/passwd
# www-data:x:33:33::/var/www:/usr/sbin/nologin  ← can't run cron jobs

# Fix: change shell temporarily or run as a different user
crontab -u root -e

Test a Job Manually as Cron Would Run It

# Simulate cron's environment
sudo -u www-data env -i HOME=/var/www SHELL=/bin/sh /bin/sh -c '/usr/local/bin/backup.sh'

# Or use run-parts to test a cron.d script
run-parts --test /etc/cron.daily

systemd Timers as a More Reliable Alternative

# Check existing timers
systemctl list-timers

# Timers log to journald: much easier to debug
journalctl -u myapp-backup.timer
journalctl -u myapp-backup.service

See the systemd timers guide for full setup instructions.

For servers that are not always running, use anacron (/etc/anacrontab) instead of standard cron. Anacron runs missed jobs on the next startup.

On this page