March 2026
A cron job that fails loudly is annoying. A cron job that fails silently is dangerous. The second kind — where the job simply stops running and nobody notices for days — is what actually causes incidents.
There are four ways to get notified. Each catches a different type of failure. For anything that matters, you want at least two.
Method 1
Cron has a built-in notification system that almost nobody uses
correctly. Set MAILTO at the top of your crontab
and cron will email you all output from every job.
# At the top of your crontab (crontab -e):
[email protected]
# This job's output (stdout + stderr) gets emailed:
0 2 * * * /usr/local/bin/backup.sh
The trick: if your job produces no output on success, you only get emails when something goes wrong. Design your scripts this way deliberately:
#!/bin/bash
# backup.sh — silent on success, loud on failure
set -euo pipefail
mysqldump mydb > /backup/mydb.sql 2>&1
# Only output on failure:
if [ ! -s /backup/mydb.sql ]; then
echo "ERROR: backup file is empty" >&2
exit 1
fi
Good for: catching errors inside your script. Bad for: knowing whether the job ran at all.
Prerequisite: your server needs a working MTA
(postfix, sendmail, msmtp). On many cloud VMs, mail isn't
configured by default. Test with:
echo "test" | mail -s "cron test" [email protected]
Method 2
Wrap your job in a script that checks the exit code and sends a notification on failure. More control than MAILTO, works with any notification channel.
#!/bin/bash
# cron-alert.sh — run a command, alert on failure
# Usage: cron-alert.sh "backup" /usr/local/bin/backup.sh
JOB_NAME="$1"
shift
OUTPUT=$("$@" 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
# Slack webhook:
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
-d "{\"text\":\"Cron failed: ${JOB_NAME} (exit ${EXIT_CODE})\n\`\`\`${OUTPUT}\`\`\`\"}"
# Or email:
echo "$OUTPUT" | mail -s "Cron failed: ${JOB_NAME}" [email protected]
fi
exit $EXIT_CODE
# In your crontab:
0 2 * * * /usr/local/bin/cron-alert.sh "nightly backup" /usr/local/bin/backup.sh
Better than MAILTO for routing alerts. Same blind spot: can't detect jobs that never started.
Method 3
Cron logs every job it runs to syslog. You can monitor these logs for missing entries or error patterns.
# Check if cron ran your job in the last hour:
grep "CRON.*backup.sh" /var/log/syslog | tail -5
# With journald:
journalctl -u cron --since "1 hour ago" | grep backup
For automated monitoring, you'd set up a separate cron job that checks whether the main job's log entries exist:
# Check every hour that the backup ran today:
0 * * * * grep -q "$(date +\%Y-\%m-\%d).*backup" /var/log/syslog || \
curl -s -X POST "$SLACK_WEBHOOK" -d '{"text":"No backup log entry today"}'
Clever but fragile. You're monitoring infrastructure with more infrastructure. If syslog breaks, you lose both the job and the alert.
Method 4
Different approach: instead of detecting failure, detect the absence of success. Your job pings an external endpoint after each successful run. If the ping stops arriving, something broke — and the alert comes from outside your infrastructure.
# Your crontab becomes:
0 2 * * * /usr/local/bin/backup.sh && curl -fsS --max-time 10 https://ping.trebben.dk/p/YOUR_TOKEN
The monitoring service tracks when it last received a ping. If the interval between pings exceeds your configured threshold, you get an alert via email, Slack, or webhook.
The only method that catches jobs which never start. The alert comes from outside your server, so it works even when everything on the server is broken.
CronPulse does exactly this.
20 monitors free. No agents, no containers, no config files.
Add && curl to your crontab — get alerted within minutes when a job stops.
It depends on what you're afraid of:
For most teams, the combination that covers the most ground with the least effort is MAILTO + heartbeat monitoring. MAILTO catches errors inside the script. Heartbeat catches everything that prevents the script from running. Together, there's no silent failure mode left.
Cron job not running? Debugging checklist → · Step-by-step monitoring setup → · Five ways cron jobs fail → · ← trebben.dk