Cron Job Monitoring: A Guide to Heartbeat Checks
Prevent silent failures in your background tasks. Learn how to implement cron job monitoring using heartbeat checks (a.k.a. a dead man's switch) to get alerted when your jobs fail or don't run on schedule. Includes practical `curl` examples.

Why Silent Failures Are a Developer's Nightmare
Cron jobs and other scheduled background tasks are the unsung heroes of modern applications. They handle critical but non-interactive work: daily database backups, weekly report generation, hourly data synchronization, and processing user uploads. They run silently in the background, and that's the problem. When they fail, they often fail silently, too.
A backup script that fails due to a permissions change, a report generator that crashes on unexpected data, or a sync job that can't reach a third-party API-these issues can go unnoticed for days or weeks. By the time you discover the problem, you may have suffered irreversible data loss, broken business processes, and lost user trust. The first you hear of it might be a customer asking, "Where is my weekly report?" or, far worse, a manager asking for a database restore from a backup that never happened.
Traditional uptime monitoring can't help here. It checks if your public-facing endpoints are online, not whether your internal, scheduled tasks are actually running successfully. For that, you need a different approach: heartbeat monitoring.
What Is Heartbeat Monitoring?
Heartbeat monitoring, also known as a "dead man's switch," flips the script on traditional checks. Instead of your monitoring service actively probing your application, your application is responsible for sending a signal-a "heartbeat"-to the monitoring service at a regular interval.
Here's how it works:
- Setup: You create a new "heartbeat check" in your monitoring service. This provides you with a unique URL.
- Integration: You modify your cron job or background task. When the job completes successfully, it makes an HTTP request to the unique URL.
- Monitoring: The monitoring service expects to receive a ping at a configured interval (e.g., every 24 hours) plus a grace period (e.g., 10 minutes).
- Alerting: If the heartbeat signal doesn't arrive within the expected time, the monitoring service creates an incident and alerts your on-call team. The job either failed to start, crashed mid-execution, or took far too long to finish.
This simple pattern is incredibly effective at detecting failures in any asynchronous or scheduled task.
How to Implement Heartbeat Checks: A Practical Example
Let's instrument a simple shell script that backs up a PostgreSQL database. We want to be alerted if the script fails or doesn't run at all.
First, we'd create a heartbeat check in a service like ServerInspector and get our unique URL, which might look like https://ping.serverinspector.com/abcdef123.
Our cron entry might look like this:
0 2 * * * /home/user/scripts/backup.sh
Now, let's modify backup.sh to send a heartbeat. The key is to use shell logic to ensure the curl command is only executed on success.
#!/bin/bash
# The unique URL from your monitoring service
HEARTBEAT_URL="https://ping.serverinspector.com/abcdef123"
# Exit immediately if a command exits with a non-zero status.
set -e
# Perform the backup
echo "Starting database backup..."
pg_dump -U myuser -h localhost mydb > /mnt/backups/mydb_$(date +%F).sql
echo "Database backup complete."
# Tidy up old backups
find /mnt/backups -type f -mtime +7 -name '*.sql' -delete
echo "Old backups cleaned up."
# If we reached here, everything succeeded. Send the heartbeat.
echo "Sending success heartbeat."
curl -fsS --retry 3 $HEARTBEAT_URL > /dev/null
By using set -e, the script will exit immediately if pg_dump or find fails. The final curl command will only be reached if all preceding commands were successful. If the script crashes or the server is down and it never runs, no heartbeat will be sent, and ServerInspector will alert you after the configured interval and grace period have passed.
Advanced Heartbeat Monitoring Techniques
You can make your heartbeat checks even more powerful by sending contextual data.
Monitoring Execution Time: Long-running jobs can be a sign of performance degradation. You can measure the job's duration and send it with your heartbeat.
#!/bin/bash
start_time=$(date +%s)
# ... your script logic here ...
end_time=$(date +%s)
duration=$((end_time - start_time))
# Send duration as a query parameter or JSON body
curl "https://ping.serverinspector.com/abcdef123?duration=${duration}"
Sending Failure Context: Instead of just failing silently, you can have the script explicitly report a failure, including the error output. This requires a more robust setup using trap or ||.
#!/bin/bash
# Using the OR operator || to catch failures
( /home/user/scripts/do_work.sh && curl https://ping.serverinspector.com/success ) || curl -d "Job failed!" https://ping.serverinspector.com/fail
In this model, your monitoring service gives you two URLs. A ping to /success resets the timer, while a ping to /fail immediately triggers an alert.
Cron Job Monitoring Checklist
Use this checklist to ensure your critical jobs are reliably monitored.
- Identify Critical Jobs: Have you listed all essential background jobs (backups, data processing, report generation, certificate renewals)?
- Unique Checks: Does each distinct job have its own unique heartbeat check URL?
- Correct Interval: Is the check configured with the correct schedule (e.g., every hour, every 24 hours)?
- Sufficient Grace Period: Is a grace period set to account for normal runtime variance and prevent false positives?
- Signal on Success: Does the job send its success ping only after all critical commands have completed successfully?
- Signal on Failure: Does the job have a mechanism to explicitly signal a failure if possible?
- Correct Alerting: Are alerts configured to notify the team responsible for that specific job?
Common Pitfalls and How to Avoid Them
Implementing heartbeat checks is straightforward, but a few common mistakes can undermine their effectiveness.
| Pitfall | The Problem | The Solution |
|---|---|---|
| Signaling Too Early | The curl command is at the start of the script. The script might fail later, but the monitoring system thinks everything is fine. | Place the success signal as the very last command of your script. Use set -e or && to ensure it only runs if preceding commands succeed. |
| Forgetting Failure Signals | You only signal success. You'll know if the job doesn't run, but you won't know immediately if it runs and fails. You have to wait for the timer to expire. | Use an ` |
| Incorrect Grace Period | A grace period that is too short causes false positive alerts every time the job runs slightly longer than average. | Analyze the job's historical runtime variation. Set a grace period that comfortably covers the 99th percentile of its duration. |
| Shared Heartbeat URLs | Using the same heartbeat URL for multiple jobs. You get an alert that "something" failed, but you don't know what. | Always use a unique heartbeat check for each distinct job. Services like ServerInspector make it easy to create and label an unlimited number of checks. |
FAQ: Cron Job & Heartbeat Monitoring
Q: What's the difference between heartbeat monitoring and uptime monitoring? A: Uptime monitoring is an active check where the monitoring service probes your application from the outside (e.g., "Is this website up?"). Heartbeat monitoring is a passive check where your application is responsible for reporting its own status from the inside (e.g., "I just finished my backup successfully."). You need both for complete coverage.
Q: Can I build my own heartbeat monitoring system? A: Yes, a basic system can be built with a simple web server that logs pings to a database and a cron job that checks for missing entries. However, a dedicated service provides a robust, reliable, and geo-distributed infrastructure for receiving pings, managing on-call schedules, and sending alerts via multiple channels (Email, SMS, Slack, Webhooks), saving you significant development and maintenance effort.
Q: How do I handle jobs that can legitimately take a variable amount of time? A: This is the primary purpose of the "grace period." You configure the check with two settings: the expected interval (e.g., every 24 hours) and a grace period (e.g., 30 minutes). The alert will only trigger if a heartbeat doesn't arrive within 24 hours and 30 minutes of the last one.
Q: What if the curl command to the heartbeat URL fails due to a network issue?
A: A robust heartbeat call should include a retry mechanism (e.g., curl --retry 3 ...). However, from the monitoring system's perspective, a failed curl is indistinguishable from a failed job-in both cases, the signal doesn't arrive. The system correctly triggers an alert, notifying you that the expected job completion did not report back, regardless of the cause.
Q: What else can I monitor with heartbeats? A: Heartbeat checks are incredibly versatile. You can use them to monitor long-running data imports, the completion of a CI/CD pipeline, periodic checks from an IoT device, or even to confirm a weekly newsletter was successfully sent to your mail provider's API.