Crontab/Linux

Cron Job Ran But Failed Silently - How to Debug

LmaDev
LmaDev
8 minutes read

Published: December 25, 2025 Category: Tutorial Author: DevOps Team Read Time: 10 minutes

CronMonitor.app - Cron Job Ran But Failed Silently - How to Debug

You check your server logs. The cron job ran. No errors. Everything looks fine at first.

Except... the database backup wasn't created. The emails weren't sent. The reports are missing.

Welcome to every developer's favorite nightmare: the "silent" cron failure.

Why Cron Jobs Fail Silently

Here's the thing - cron is outdated. Like old Unix from the 70s. It wasn't designed with helpful error messages in mind. When a cron job runs, for cron it means the task was executed (correctly or not).

Cron doesn't verify whether the script you added executed correctly or if any errors occurred during execution. To put it bluntly, that's not cron's job, and it doesn't have any mechanisms to verify the correctness of executed tasks. Cron is a simple service whose only task is to execute a job at a specified time.

This is different from manually running a script in the terminal. When you're logged in, you see errors immediately. Script crashes? You know. Database connection fails? You see it right there.

But cron runs in a different environment. No terminal. No user session. No error output unless you explicitly specify where it should be sent.

The Most Common Causes

1. Missing Environment Variables

This is the main problem. Your script works perfectly when you run it manually because your user session has all environment variables loaded - PATH, database credentials, API keys, etc.

Cron? It runs in a minimal environment. Your $PATH probably only includes /usr/bin and /bin. All those environment variables you set in .bashrc or .bash_profile won't be loaded and thus won't be available in the cron session.

So your script tries to run php, python, or node, cron can't find them, and the job simply won't execute.

2. File Paths Are Relative

You wrote:

./backup-script.sh

Works fine from your home directory. But cron doesn't run from your home directory. It runs from who-knows-where (usually / or the user's home directory, but don't count on it).

That relative path? Broken. Script not found. Silent failure.

3. Permission Problems

Your user can read the database config file. Your user can write to the backup directory.

But which user is cron running as? Is it the same user? Does that user have the right permissions?

4. The Script Actually Failed But You're Not Capturing Errors

Your script encountered an error. Threw an exception. Exited with a non-zero status code.

But where did that error message go? Probably into the void. Unless you told cron to save it somewhere.

How to Debug a Cron Job in Practice

Step 1: Check If It Actually Ran

First, make sure the cron job actually executed:

grep CRON /var/log/syslog

or on some systems:

grep CRON /var/log/cron

You should see entries like:

Dec 25 02:00:01 CRON[12345]: (username) CMD (/path/to/your/script.sh)

If you don't see these entries, your cron job isn't running at all. Check your crontab syntax.

If you DO see these entries, congratulations - cron executed your job. Now we need to figure out why it failed.

Step 2: Capture the Output

Modify your crontab to save both stdout and stderr to a log file:

0 2 * * * /path/to/script.sh >> /var/log/my-cron.log 2>&1

That 2>&1 is important - it redirects stderr (errors) to stdout, so everything goes to your log file.

Now wait for the next run, or trigger it manually to test:

# Run the cron job as the cron user would
sudo -u www-data /path/to/script.sh

Check your log file. You'll probably see the actual error now.

Step 3: Fix the Environment

If you're seeing "command not found" errors, you need to do one of the following:

Option A: Use absolute paths for everything

0 2 * * * /usr/bin/php /var/www/html/backup.php

Option B: Set the PATH variable in your crontab

PATH=/usr/local/bin:/usr/bin:/bin
0 2 * * * php /var/www/html/backup.php

Option C: Load environment variables in your script

0 2 * * * /bin/bash -l -c '/path/to/script.sh'

The -l flag makes bash act like a login shell, loading your profile.

Step 4: Test in Cron's Environment

Here's a trick - make cron dump its entire environment to a file:

* * * * * env > /tmp/cron-env.txt

Run this for one minute, then check /tmp/cron-env.txt. Compare it to your normal environment (run env in your terminal).

You'll see what's missing. Usually it's a lot.

Step 5: Add Proper Error Handling to Your Script

Instead of letting your script fail silently, make it loud:

Bash example:

#!/bin/bash
set -e  # Exit on any error
set -u  # Exit on undefined variable

# Your commands here
echo "Starting backup at $(date)"

PHP example:

<?php
// Log everything
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php-cron-errors.log');

try {
    // Your code here
    backupDatabase();
    echo "Backup completed\n";
} catch (Exception $e) {
    error_log("Backup failed: " . $e->getMessage());
    exit(1);
}

Python example:

import logging
import sys

logging.basicConfig(
    filename='/var/log/python-cron.log',
    level=logging.INFO,
    format='%(asctime)s - %(message)s'
)

try:
    # Your code here
    backup_database()
    logging.info("Backup completed")
except Exception as e:
    logging.error(f"Backup failed: {str(e)}")
    sys.exit(1)

Step 6: Check Exit Codes

Your script might be running but exiting with an error code. Check the last exit code:

0 2 * * * /path/to/script.sh; echo $? >> /var/log/exit-codes.log

Exit code 0 = success. Anything else = problem.

Prevention is Better Than Debugging

Once you've fixed your silent failure, here's how to prevent it from happening again:

  1. Always redirect output to a log file - Don't rely on cron's email. Seriously, just don't.

  2. Use absolute paths - Every. Single. Time. No exceptions.

  3. Set up proper logging in your scripts - Don't just echo to stdout. Write to actual log files with timestamps.

  4. Test your scripts in cron's environment - Use sudo -u to run as the cron user before adding to crontab.

  5. Monitor your cron jobs - This is where tools like CronMonitor come in handy. Instead of checking logs manually, you get instant alerts when a job doesn't run or fails. Set up a health check URL that your script hits when it completes successfully. If CronMonitor doesn't receive that ping, you know something went wrong.

  6. Document your dependencies - Keep a comment in your crontab about what environment variables or permissions are needed.

The "It Works on My Machine" Checklist

Before you declare your cron job fixed, test these:

  • [ ] Does it work when run manually?
  • [ ] Does it work when run with sudo -u cron-user?
  • [ ] Are all paths absolute?
  • [ ] Are all required environment variables set?
  • [ ] Does the user have permission to read/write all necessary files?
  • [ ] Is output being logged somewhere you can actually check?
  • [ ] Have you tested it after a server reboot?

When All Else Fails

Sometimes you've tried everything and it still doesn't work. Here's your nuclear option:

Create a wrapper script that sets up everything:

#!/bin/bash
# cron-wrapper.sh

# Set environment
export PATH=/usr/local/bin:/usr/bin:/bin
export DATABASE_URL="your-connection-string"
source /home/user/.env

# Change to script directory
cd /var/www/html

# Run with logging
/usr/bin/php artisan schedule:run >> /var/log/laravel-cron.log 2>&1

# Check if it worked
if [ $? -eq 0 ]; then
    echo "Success at $(date)" >> /var/log/cron-success.log
else
    echo "Failed at $(date)" >> /var/log/cron-failure.log
fi

Then in your crontab:

0 2 * * * /path/to/cron-wrapper.sh

Is it overkill? Maybe. Does it work? Absolutely.

Wrapping Up

Silent cron failures are frustrating because the lack of feedback makes debugging feel like detective work. But once you understand that cron runs in a completely different environment from your normal shell, the solutions become clearer.

The key steps: capture the output, use absolute paths, set environment variables explicitly, and add proper error handling to your scripts.

And next time you're SSH-ing into a server at 2 AM to check why a backup didn't run, remember: there are monitoring tools that can alert you before you even notice the problem. Your future sleep-deprived self will thank you.

Share this article