← trebben.dk

How to get alerted when a cron job fails

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

Built-in cron email (MAILTO)

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

Catches

  • Script errors (non-zero exit)
  • Unexpected output
  • Stderr messages

Misses

  • Job never starts (cron down)
  • Server unreachable
  • Crontab deleted
  • Disk full before job runs

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

Wrapper script with Slack/email on failure

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

Catches

  • Non-zero exit codes
  • Script output on failure
  • Works with Slack, PagerDuty, etc.

Misses

  • Job never starts
  • Wrapper itself fails
  • Server offline

Better than MAILTO for routing alerts. Same blind spot: can't detect jobs that never started.

Method 3

Log monitoring (syslog + grep)

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

Heartbeat monitoring

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.

Catches

  • Script failures (no ping sent)
  • Job never starts (no ping sent)
  • Cron daemon down
  • Server offline
  • Disk full, OOM kill, etc.

Misses

  • Job runs but produces bad data
  • Monitoring service itself is down

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.

Start monitoring →

Which method to use

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