How to Fix Linux Cron Job Silently Failing Without Sending Output Logs or Emails
Quick answer
A cron job isn't doing what it's supposed to, but there's no error output, no log entry, and no email notification pointing you toward why. Cron is designed to...
A cron job isn't doing what it's supposed to, but there's no error output, no log entry, and no email notification pointing you toward why. Cron is designed to email job output by default, but that mechanism depends on a working local mail setup that most modern servers simply don't have configured β which means failures vanish silently unless you explicitly redirect output somewhere else.
The Problem
The scheduled job clearly isn't running correctly (the expected side effect, like a generated file or database update, never happens), but there's nothing in the usual places explaining why:
$ crontab -l
0 2 * * * /home/user/scripts/backup.sh
$ mail
No mail for user
Checking the system's cron log shows the job did run, at least according to cron's own bookkeeping:
$ grep CRON /var/log/syslog | tail -5
Aug 7 02:00:01 myserver CRON[2841]: (user) CMD (/home/user/scripts/backup.sh)
Why It Happens
Cron's default behavior is to capture a job's stdout and stderr and email them to the crontab owner, but this only works if the system actually has a functioning mail transport agent (MTA) like postfix or sendmail configured to deliver mail somewhere you'll actually see it β and most modern cloud servers don't have one set up at all, since they're not typically used as mail servers. Without a working MTA, cron's attempt to email output just fails silently, with no fallback and no separate error about the mail delivery itself failing. Other contributing causes:
- Cron IS emailing output, but to local mail delivery (
/var/mail/user) that nobody ever checks, rather than an external address you'd actually see. - The script itself redirects its own output somewhere (or suppresses it entirely with something like
> /dev/null 2>&1) that was added at some point and forgotten about, silencing exactly the output cron would otherwise have captured and emailed. - The job is failing at a point before it would produce any output at all β for example, cron's minimal environment lacking a dependency the script needs, causing an immediate, silent failure.
The Fix
The most direct fix, and generally the better long-term practice regardless of mail configuration, is explicitly redirecting each job's output to a dedicated log file rather than relying on cron's email mechanism at all:
0 2 * * * /home/user/scripts/backup.sh >> /home/user/logs/backup.log 2>&1
This captures both standard output and errors into a persistent log file you can check directly, completely independent of whether mail delivery works on the system at all. Check it after the next scheduled run:
tail -50 /home/user/logs/backup.log
If you do want email notifications and are willing to set up an MTA, configure MAILTO at the top of your crontab to direct output to a specific address rather than relying on local delivery:
MAILTO=you@example.com
0 2 * * * /home/user/scripts/backup.sh
For this to actually work, you need a functioning MTA capable of relaying outbound mail β installing and configuring msmtp as a lightweight relay-only mail sender (rather than a full mail server) is a common, low-overhead way to get this working on a server that just needs to send occasional notification emails:
sudo apt install msmtp msmtp-mta
sudo nano /etc/msmtprc
# /etc/msmtprc β configure with your actual SMTP relay credentials
account default
host smtp.your-provider.com
port 587
auth on
user you@example.com
password your_smtp_password
tls on
Test that mail delivery actually works before relying on it for cron notifications:
echo "test message" | mail -s "Test" you@example.com
If the script itself is silencing its own output, check for and remove any unintentional redirection to /dev/null left over from earlier debugging or a copy-pasted template:
grep -n "dev/null\|2>&1" /home/user/scripts/backup.sh
Still Not Working?
If the job is failing before producing any meaningful output at all, add explicit logging at the very start of the script to confirm it's actually being invoked and to capture the earliest possible failure point, since cron's minimal environment (missing PATH entries, unset environment variables) is a common source of scripts failing immediately, before they'd normally log anything useful:
#!/bin/bash
echo "$(date): backup.sh started" >> /home/user/logs/backup.log
set -x # print every command as it executes, for maximum visibility during debugging
# ... rest of script
echo "$(date): backup.sh completed" >> /home/user/logs/backup.log
If the log shows the "started" line but never reaches "completed," the script is failing somewhere in between, and the set -x trace output captured in the log will show exactly which command was executing when it stopped β far more actionable than the complete silence you started with.