Skip to content
Go back

Why Your Cron Job Is Failing Silently

By SumGuy 5 min read
Why Your Cron Job Is Failing Silently

You wrote a cron job. It runs fine when you execute it manually. But cron never runs it. Or it runs silently and produces nothing.

Welcome to cron debugging, where the same script works on your terminal and breaks in cron’s environment.

The Environment Problem

When you log in and run a command, you have:

Cron has:

So your cron job tries to run docker and cron has no idea what that is.

Fix 1: Use Absolute Paths

This is the number one fix. Don’t do this:

Terminal window
0 2 * * * backup.sh

Do this:

Terminal window
0 2 * * * /usr/local/bin/backup.sh

Or find the absolute path:

/usr/local/bin/python3
which python
which docker
# /usr/bin/docker

Then use full paths in your cron job.

Fix 2: Set Your PATH

Add PATH to your crontab:

Terminal window
crontab -e

At the top, before any jobs:

Terminal window
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SHELL=/bin/bash
0 2 * * * /usr/local/bin/backup.sh

Now cron knows where everything is.

Fix 3: Redirect Output to a Log File

Cron suppresses stdout. If your script prints something, you never see it. Redirect to a file:

Terminal window
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

The 2>&1 redirects errors too. Check the file:

Terminal window
tail -f /var/log/backup.log

Now you can see what’s happening.

Fix 4: Set MAILTO (Or Don’t)

By default, cron emails you any output. If you don’t have mail set up, the output disappears into a black hole.

Disable mail:

Terminal window
MAILTO=""
0 2 * * * /usr/local/bin/backup.sh

Or send to a specific email:

Terminal window
MAILTO="admin@example.com"
0 2 * * * /usr/local/bin/backup.sh

But honestly, redirecting to a log file is better.

Example: Backup Script

Bad (won’t work):

Terminal window
0 2 * * * backup.sh

Better:

Terminal window
0 2 * * * /home/admin/scripts/backup.sh >> /var/log/backup.log 2>&1

Good:

Terminal window
0 2 * * * /home/admin/scripts/backup.sh >> /var/log/backup.log 2>&1 && echo "Backup completed at $(date)" >> /var/log/backup.log || echo "Backup failed at $(date)" >> /var/log/backup.log

Testing Your Cron Job

Before you schedule it, test it in cron’s environment:

Terminal window
# Run with cron's minimal environment
env -i /bin/sh -c '/path/to/script.sh'
# Or see what PATH cron sees
(crontab -l | grep -v ^# | head -1; echo 'echo $PATH') | at now + 1 minute

Better yet, create a test cron that runs every minute:

Terminal window
* * * * * /usr/local/bin/backup.sh >> /tmp/test_backup.log 2>&1

Wait a minute, check the log:

Terminal window
cat /tmp/test_backup.log

Once it works, change it to your desired schedule.

Common Cron Gotchas

Command not found

Your script uses docker but cron doesn’t know where it is.

Terminal window
Fix: /usr/bin/docker ps

No $HOME

Cron doesn’t set your home directory. Scripts that reference ~/something break.

Terminal window
# Don't do this
cp ~/backup.conf /tmp/
# Do this
cp /home/admin/backup.conf /tmp/

Commands with pipes or redirects fail

Cron runs jobs with /bin/sh -c, not /bin/bash. Some constructs don’t work.

Terminal window
# Make sure to explicitly call bash if needed
0 2 * * * /bin/bash -c "docker ps | grep backup > /tmp/status.txt"

Symlinks to scripts

Symlinks might not resolve in cron’s environment.

Terminal window
# Don't do this
0 2 * * * ~/backup -> /home/admin/scripts/backup.sh
# Do this
0 2 * * * /home/admin/scripts/backup.sh

Scripts with relative paths

If your script changes directories, use absolute paths:

Terminal window
# Bad
0 2 * * * cd /data && ./process.sh
# Good
0 2 * * * cd /data && /data/process.sh

No TTY

Cron runs without a terminal. Commands like sudo that need a TTY fail silently.

Use sudo’s NOPASSWD option in sudoers, or run cron as root.

A Real-World Cron Job

#!/bin/bash
set -e
# Logging
LOG_FILE="/var/log/docker_cleanup.log"
{
echo "=== Docker cleanup started at $(date) ==="
# Clean up old images (7+ days old)
docker image prune -a --filter "until=168h" -f
# Clean up dangling volumes
docker volume prune -f
# Log disk usage
echo "Disk usage after cleanup:"
docker system df
echo "=== Docker cleanup completed at $(date) ==="
} >> "$LOG_FILE" 2>&1

Then in crontab:

Terminal window
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""
# Run at 2 AM daily
0 2 * * * /home/admin/scripts/docker_cleanup.sh

Debugging Active Cron Jobs

If a cron job is running but you don’t know why it’s failing:

Terminal window
# List all cron jobs for all users
sudo grep CRON /var/log/syslog | tail -20
# Or on systemd systems:
sudo journalctl -u cron | tail -50
# Check a specific user's crons:
sudo crontab -u username -l

The Checklist

Before you schedule a cron job:

Cron jobs that fail silently are a special kind of frustrating. But they’re avoidable. Use absolute paths, redirect output, and test before you schedule. Your 3 AM self will thank you when the backup actually runs.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Cleaning Up Docker Disk Space the Right Way
Next Post
SSH Multiplexing: Stop Reconnecting Every Time

Related Posts