Your database backup cron job has been failing silently for 3 weeks. You discover this when you actually need to restore from backup.
Game over.
This scenario plays out constantly in production systems. Cron jobs fail silently. No alerts. No visibility. No accountability. You only discover problems when disaster strikes.
Professional developers don't operate this way.
They monitor every cron job. They get instant alerts when things fail. They track execution history. They measure performance over time.
This guide shows you exactly how to monitor your cron jobs like a pro—from basic logging to sophisticated monitoring platforms that catch failures before they become catastrophes.
The Problem: Silent Failures
Cron jobs are dangerous because they fail silently:
Scenario 1: The Forgotten Backup
0 2 * * * /usr/local/bin/backup-database.sh
What you think happens:
- ✅ Runs every night at 2 AM
- ✅ Creates database backup
- ✅ Everything is protected
What actually happens:
- ❌ Script has a typo (fails immediately)
- ❌ No output produced
- ❌ No alerts sent
- ❌ You have no backups for months
- ❌ You discover this when you need to restore
Scenario 2: The Broken PATH
0 3 * * * python /home/user/scripts/process-data.py
Reality:
- Cron can't find
python(not in PATH) - Script never runs
- Data processing stops
- Business reports are empty
- Nobody notices for weeks
The Core Problem
Cron doesn't tell you when jobs fail. It runs the command and moves on. No logging. No alerts. No confirmation.
You need monitoring.
Level 1: Basic Logging (Better Than Nothing)
The absolute minimum: redirect output to a log file.
Simple Output Logging
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
What this does:
>>appends output to log file2>&1captures errors too- Every execution leaves a record
Check logs manually:
tail -f /var/log/backup.log
Timestamped Logging
Better: add timestamps to your scripts.
#!/bin/bash
# File: /usr/local/bin/backup.sh
LOG_FILE="/var/log/backup.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
log "Starting backup..."
if /usr/bin/mysqldump mydb > /backups/db.sql; then
log "SUCCESS: Backup completed"
else
log "ERROR: Backup failed"
exit 1
fi
log "Backup finished"
Cron entry:
0 2 * * * /usr/local/bin/backup.sh
Output in log:
[2025-01-16 02:00:01] Starting backup...
[2025-01-16 02:03:47] SUCCESS: Backup completed
[2025-01-16 02:03:47] Backup finished
Pros:
- ✅ Simple to implement
- ✅ No external dependencies
- ✅ Searchable history
Cons:
- ❌ Must manually check logs
- ❌ No alerts on failure
- ❌ Logs can grow huge
- ❌ Easy to ignore/forget
Level 2: Email Notifications on Failure
Get notified when something breaks.
Cron's Built-in Email (If Configured)
Set MAILTO in your crontab:
MAILTO=admin@example.com
0 2 * * * /usr/local/bin/backup.sh
If the job produces output, cron emails it to you.
Problems:
- Most servers don't have mail configured
- You get email for EVERY execution (spam)
- Hard to distinguish success from failure
Better: Only Email on Failure
0 2 * * * /usr/local/bin/backup.sh || echo "Backup failed!" | mail -s "BACKUP FAILURE" admin@example.com
How it works:
||means "if previous command fails, run this"- Only sends email when backup returns non-zero exit code
Email with Details
#!/bin/bash
# File: /usr/local/bin/backup-with-email.sh
EMAIL="admin@example.com"
LOG_FILE="/var/log/backup.log"
# Run backup
if /usr/local/bin/backup.sh >> "$LOG_FILE" 2>&1; then
# Success - do nothing
exit 0
else
# Failure - send email with logs
cat "$LOG_FILE" | mail -s "BACKUP FAILED on $(hostname)" "$EMAIL"
exit 1
fi
Pros:
- ✅ Automatic alerts
- ✅ Only emails on failures
- ✅ No external services needed
Cons:
- ❌ Requires mail server setup
- ❌ Email can be delayed
- ❌ No history/dashboard
- ❌ Can't track trends
Level 3: Dead Man's Switch Monitoring
The professional approach: heartbeat monitoring.
How It Works
- Your cron job makes an HTTP request when it SUCCEEDS
- Monitoring service expects regular heartbeats
- If heartbeat stops, you get alerted
This catches two failure modes:
- Script fails (doesn't send heartbeat)
- Script doesn't run at all (missing heartbeat)
Using Cronitor (Recommended)
Step 1: Set up a monitor at cronitor.io
Create a monitor with expected schedule (e.g., "daily at 2 AM").
Step 2: Get your unique ping URL:
https://cronitor.link/p/abc123xyz/backup-job
Step 3: Modify your script to ping on success:
#!/bin/bash
# File: /usr/local/bin/monitored-backup.sh
CRONITOR_URL="https://cronitor.link/p/abc123xyz/backup-job"
# Run backup
if /usr/bin/mysqldump mydb | gzip > /backups/db_$(date +%Y%m%d).sql.gz; then
# Success - send heartbeat
curl -fsS -m 10 --retry 5 "$CRONITOR_URL"
else
# Failure - ping failure endpoint
curl -fsS -m 10 --retry 5 "$CRONITOR_URL/fail"
exit 1
fi
What Cronitor monitors:
- ✅ Did the job run on schedule?
- ✅ Did it succeed or fail?
- ✅ How long did it take?
- ✅ Any missing executions?
Alerts:
- SMS
- Slack
- PagerDuty
- Webhooks
Using Healthchecks.io (Free Alternative)
Similar concept, free for personal use:
#!/bin/bash
HEALTHCHECK_URL="https://hc-ping.com/your-uuid-here"
# Ping start
curl -fsS -m 10 --retry 5 "$HEALTHCHECK_URL/start"
# Run your job
if /usr/local/bin/backup.sh; then
# Ping success
curl -fsS -m 10 --retry 5 "$HEALTHCHECK_URL"
else
# Ping failure
curl -fsS -m 10 --retry 5 "$HEALTHCHECK_URL/fail"
fi
Benefits:
- ✅ Free for up to 20 checks
- ✅ Email and Slack alerts
- ✅ Simple dashboard
- ✅ No credit card required
Level 4: Execution Tracking and Metrics
Track performance over time.
Log Execution Duration
#!/bin/bash
# File: /usr/local/bin/timed-backup.sh
LOG_FILE="/var/log/backup-metrics.log"
START_TIME=$(date +%s)
# Run backup
/usr/local/bin/backup.sh
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo "$(date '+%Y-%m-%d %H:%M:%S'),SUCCESS,$DURATION" >> "$LOG_FILE"
Output:
2025-01-16 02:00:00,SUCCESS,187
2025-01-17 02:00:00,SUCCESS,192
2025-01-18 02:00:00,SUCCESS,245
Analyze trends:
# Average backup time
awk -F, '{sum+=$3; count++} END {print sum/count}' /var/log/backup-metrics.log
# Slowest backup
awk -F, '{print $3,$1}' /var/log/backup-metrics.log | sort -rn | head -1
Database Logging
Store execution history in a database for better analytics:
#!/bin/bash
# File: /usr/local/bin/db-logged-backup.sh
START_TIME=$(date +%s)
START_DATE=$(date '+%Y-%m-%d %H:%M:%S')
# Run backup
if /usr/local/bin/backup.sh; then
STATUS="success"
EXIT_CODE=0
else
STATUS="failed"
EXIT_CODE=$?
fi
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# Log to database
mysql -u logger -p'password' monitoring <<EOF
INSERT INTO cron_executions (job_name, start_time, duration, status, exit_code)
VALUES ('backup', '$START_DATE', $DURATION, '$STATUS', $EXIT_CODE);
EOF
Query execution history:
-- Recent executions
SELECT * FROM cron_executions
WHERE job_name = 'backup'
ORDER BY start_time DESC
LIMIT 10;
-- Success rate last 30 days
SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) as successes,
ROUND(100.0 * SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) / COUNT(*), 2) as success_rate
FROM cron_executions
WHERE job_name = 'backup'
AND start_time > DATE_SUB(NOW(), INTERVAL 30 DAY);
Level 5: Comprehensive Monitoring Platform
For production systems with many cron jobs.
Monitoring multiple jobs:
Features:
- Monitor unlimited cron jobs
- Execution history dashboard
- Performance metrics
- Uptime monitoring
- Error rate tracking
- Team notifications
- Incident management
- Status pages
Monitoring multiple jobs:
# Each job pings unique URL
0 2 * * * /usr/local/bin/backup.sh && curl https://cronitor.link/p/abc123/backup
0 3 * * * /usr/local/bin/cleanup.sh && curl https://cronitor.link/p/abc123/cleanup
0 4 * * * /usr/local/bin/reports.sh && curl https://cronitor.link/p/abc123/reports
Dashboard shows:
- Which jobs ran successfully
- Which failed
- Which didn't run at all
- Execution duration trends
- Historical reliability
Better Uptime (Alternative)
Similar features with phone call alerts:
Unique feature: Calls your phone if critical jobs fail (not just email/SMS).
Setup:
# Heartbeat monitoring
0 2 * * * /usr/local/bin/critical-backup.sh && curl https://betteruptime.com/api/v1/heartbeat/your-token
Alert escalation:
- Email immediately
- SMS after 5 minutes
- Phone call after 10 minutes
- Alert backup contacts after 15 minutes
Monitoring Best Practices
1. Monitor Critical Jobs First
Don't try to monitor everything at once.
Start with:
- Database backups
- Data synchronization
- Financial calculations
- Security scans
- Customer-facing features
Add others gradually.
2. Set Realistic Expectations
Cronitor/Healthchecks need to know when to expect heartbeats.
Daily at 2 AM → Grace period: 15 minutes
Every 5 minutes → Grace period: 2 minutes
Weekly on Sunday → Grace period: 4 hours
If job doesn't ping within grace period, you get alerted.
3. Alert the Right People
Not every failure needs to wake someone up at 3 AM.
Severity levels:
- Critical: Database backups, financial processing → Phone call
- High: Data sync, reports → SMS
- Medium: Cache clearing, logs → Email
- Low: Cleanup tasks → Dashboard only
4. Test Your Monitoring
Verify alerts actually work:
# Trigger a test failure
0 2 * * * /usr/local/bin/backup.sh && curl https://cronitor.link/abc123/backup/fail
Check that:
- Alert email arrives
- Alert has useful information
- Alert reaches right people
- Escalation works
5. Review Execution History
Set up weekly reviews:
# Weekly report of all cron failures
0 9 * * 1 /usr/local/bin/weekly-cron-report.sh
Look for patterns:
- Jobs failing consistently?
- Increasing execution time?
- Missed executions?
- New errors?
Complete Monitoring Setup Example
Here's a production-ready monitored cron job:
#!/bin/bash
# File: /usr/local/bin/production-backup.sh
set -euo pipefail
# Configuration
BACKUP_DIR="/backups/mysql"
LOG_FILE="/var/log/backup.log"
CRONITOR_URL="https://cronitor.link/p/abc123/backup"
ALERT_EMAIL="ops@example.com"
DB_NAME="production"
# Logging function
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
# Start monitoring
START_TIME=$(date +%s)
log "Starting backup for $DB_NAME"
curl -fsS -m 10 "$CRONITOR_URL/run" || true
# Check disk space
AVAILABLE=$(df /backups | tail -1 | awk '{print $4}')
if [ "$AVAILABLE" -lt 10485760 ]; then
log "ERROR: Low disk space"
curl -fsS "$CRONITOR_URL/fail?msg=Low+disk+space" || true
echo "Low disk space: ${AVAILABLE}KB" | mail -s "BACKUP ALERT" "$ALERT_EMAIL"
exit 1
fi
# Run backup
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_$(date +%Y%m%d_%H%M%S).sql.gz"
if mysqldump --single-transaction "$DB_NAME" | gzip > "$BACKUP_FILE"; then
# Verify backup
if [ -s "$BACKUP_FILE" ]; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
log "SUCCESS: Backup completed ($SIZE)"
# Cleanup old backups (keep 7 days)
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +7 -delete
log "Cleaned up old backups"
# Report success
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
curl -fsS "$CRONITOR_URL?duration=$DURATION" || true
exit 0
else
log "ERROR: Backup file is empty"
curl -fsS "$CRONITOR_URL/fail?msg=Empty+backup" || true
exit 1
fi
else
log "ERROR: mysqldump failed"
curl -fsS "$CRONITOR_URL/fail?msg=mysqldump+failed" || true
tail -20 "$LOG_FILE" | mail -s "BACKUP FAILED" "$ALERT_EMAIL"
exit 1
fi
Cron entry:
MAILTO=ops@example.com
0 2 * * * /usr/local/bin/production-backup.sh
This setup provides:
- ✅ Detailed logging with timestamps
- ✅ Disk space checks before backup
- ✅ Backup verification
- ✅ Automatic cleanup of old backups
- ✅ Heartbeat monitoring (Cronitor)
- ✅ Email alerts on failure
- ✅ Duration tracking
- ✅ Error messages in alerts
Monitoring Checklist
Before deploying any critical cron job:
- [ ] Logging with timestamps enabled
- [ ] Log rotation configured
- [ ] Exit codes properly set (0=success, non-zero=failure)
- [ ] Heartbeat monitoring configured
- [ ] Alert contacts defined
- [ ] Alert severity set appropriately
- [ ] Test failure scenario works
- [ ] Dashboard shows execution history
- [ ] Grace periods set correctly
- [ ] Documentation updated
Conclusion: Never Fly Blind Again
The problem: Cron jobs fail silently. You discover issues weeks later when you need the data.
The solution: Monitor everything.
Levels of monitoring:
- Basic: Log files with timestamps
- Better: Email notifications on failure
- Good: Heartbeat monitoring (Cronitor/Healthchecks)
- Best: Full monitoring platform with metrics
Your next steps:
- Add logging to all cron jobs (at minimum)
- Set up heartbeat monitoring for critical jobs (Cronitor/Healthchecks)
- Configure alerts to the right people
- Test your monitoring by triggering failures
- Review dashboards weekly
Professional monitoring transforms:
| Without Monitoring | With Monitoring | |-------------------|-----------------| | "Did backup run?" → "No idea" | "Yes, 187 seconds, 100% success rate" | | "When did it fail?" → "Unknown" | "First failure: Jan 3 at 2:13 AM" | | "How often?" → "Can't tell" | "14 successes, 1 failure (93% uptime)" | | Discovery time → Weeks later | Alert → 30 seconds |
Stop wondering. Start monitoring.
Ready to create reliable cron schedules? Use our Cron Expression Generator to build perfect schedules, then monitor them with Cronitor!
Related Articles
Monitoring & Logging:
- Where Did My Cron Job Output Go? - Complete logging guide
- Why Your Cron Job Isn't Running - Troubleshooting failures
Production Best Practices:
- Secure Your Cron Jobs from Attacks - Security hardening
- Automate Database Backups with Cron - Backup automation
- 10 Essential Cron Jobs for Web Developers - Must-have automation
Fundamentals:
- The Ultimate Guide to Cron Jobs - Complete tutorial
- Cron Operators Explained - Syntax guide
- Environment Variables in Cron Jobs - Fix PATH issues
Keywords: cron job monitoring, monitor cron jobs, cron job alerts, cron job notifications, cronitor alternative, healthchecks cron, cron job logging, cron monitoring tools, cron heartbeat monitoring