You added a cron job. It should be running. It isn’t. The logs are silent. This is one of the most frustrating debugging experiences in Linux administration because cron fails silently by default.

Here’s a systematic checklist to find the problem.

1. Is crond Actually Running?

First, verify the cron daemon is running:

1
2
3
4
5
6
7
# systemd systems
systemctl status cron
# or
systemctl status crond

# Older init systems
service cron status

If it’s not running, start it:

1
2
sudo systemctl start cron
sudo systemctl enable cron

2. Is Your Crontab Syntax Correct?

The most common mistake is incorrect syntax. Cron format is:

MINHOURDOMMONDOWcommand
  • MIN: 0-59
  • HOUR: 0-23
  • DOM: Day of month (1-31)
  • MON: Month (1-12)
  • DOW: Day of week (0-7, where 0 and 7 are Sunday)

Common mistakes:

1
2
3
4
5
6
7
8
9
# WRONG: Using * * * * * * (6 fields)
* * * * * * /path/to/script.sh

# RIGHT: 5 fields for timing
* * * * * /path/to/script.sh

# WRONG: Forgetting that DOM and DOW are OR'd, not AND'd
# This runs on the 15th AND every Monday, not just Monday the 15th
0 9 15 * 1 /path/to/script.sh

Validate your syntax:

1
2
3
4
5
# List your crontab
crontab -l

# Edit with syntax checking
crontab -e

3. Is the Script Executable?

1
2
3
4
5
# Check permissions
ls -la /path/to/script.sh

# Make it executable if needed
chmod +x /path/to/script.sh

4. Are You Using Absolute Paths?

Cron runs with a minimal environment. This is the #1 cause of “works manually, fails in cron”:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# WRONG: Relies on PATH
python script.py

# RIGHT: Absolute paths everywhere
/usr/bin/python3 /home/user/scripts/script.py

# WRONG: Relative paths
cd scripts && ./run.sh

# RIGHT: Absolute paths
cd /home/user/scripts && /home/user/scripts/run.sh

Find your binary paths:

1
2
3
which python3
which node
which bash

5. Is the Environment Different?

Cron’s environment is minimal. Your interactive shell has PATH, environment variables, and shell configurations that cron doesn’t.

Debug by capturing the environment:

1
2
# Add this as a cron job to see what cron sees
* * * * * env > /tmp/cron-env.txt

Compare with your shell:

1
2
env > /tmp/shell-env.txt
diff /tmp/shell-env.txt /tmp/cron-env.txt

Fix by setting variables in the crontab:

1
2
3
4
5
# Set PATH at the top of your crontab
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash

* * * * * /path/to/script.sh

Or source your profile in the command:

1
* * * * * . /home/user/.bashrc && /path/to/script.sh

6. Are You Capturing Output?

Cron sends output to the user’s mail by default. If mail isn’t configured, output vanishes into the void.

Always redirect output:

1
2
3
4
5
# Capture stdout and stderr to a log file
* * * * * /path/to/script.sh >> /var/log/myscript.log 2>&1

# Or discard if you truly don't care
* * * * * /path/to/script.sh > /dev/null 2>&1

7. Check the Cron Logs

1
2
3
4
5
6
7
8
# Debian/Ubuntu
grep CRON /var/log/syslog

# RHEL/CentOS
grep CRON /var/log/cron

# Or use journalctl
journalctl -u cron --since "1 hour ago"

Look for entries showing your job was triggered:

Mar2514:01:01serverCRON[12345]:(user)CMD/path/to/script.sh)

If you see the CMD line but the script doesn’t work, the problem is in the script or environment.

If you don’t see the CMD line, the crontab isn’t being read correctly.

8. User vs System Crontab

There are two types of crontabs:

User crontab (edited with crontab -e):

  • Stored in /var/spool/cron/crontabs/username
  • Runs as that user
  • No user field in the syntax

System crontab (files in /etc/cron.d/ or /etc/crontab):

  • Requires a user field
  • Has slightly different syntax:
1
2
# /etc/cron.d/myjob - note the username field
* * * * * root /path/to/script.sh

9. Check for Errors in /etc/cron.d/

Files in /etc/cron.d/ must:

  • Be owned by root
  • Not be writable by group or others
  • Not have a . in the filename (some systems ignore them)
  • Have a newline at the end of the file
1
2
3
4
5
6
# Check permissions
ls -la /etc/cron.d/

# Check for trailing newline
tail -c 1 /etc/cron.d/myjob | od -c
# Should show: 0000000  \n

10. The Nuclear Option: Wrapper Script

When all else fails, create a wrapper that sets up the environment explicitly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/bash
# /home/user/scripts/cron-wrapper.sh

# Set up the environment
export PATH=/usr/local/bin:/usr/bin:/bin
export HOME=/home/user
source /home/user/.bashrc

# Log execution
echo "$(date): Starting job" >> /var/log/myjob.log

# Run the actual script
/home/user/scripts/actual-script.sh >> /var/log/myjob.log 2>&1

# Log completion
echo "$(date): Finished with exit code $?" >> /var/log/myjob.log

Then your crontab becomes:

1
* * * * * /home/user/scripts/cron-wrapper.sh

Quick Debugging Template

Add this temporarily to catch everything:

1
* * * * * /bin/bash -c 'echo "=== $(date) ===" >> /tmp/crontest.log && whoami >> /tmp/crontest.log && pwd >> /tmp/crontest.log && env >> /tmp/crontest.log && /path/to/your/script.sh >> /tmp/crontest.log 2>&1'

This logs: timestamp, user, working directory, full environment, and script output.


The pattern with cron is always the same: it runs in a stripped-down environment with no assumptions. Debug by making everything explicit—paths, environment variables, output redirection—and you’ll find the problem.