Cron Job Monitoring with Heartbeats: Detect Missing Work Without Hiding Failures
A scheduled process can start regularly and still fail to deliver its result. Place the signal after useful work and test what happens when it never arrives.
Send the heartbeat after useful work succeeds
A scheduler log that says “started” does not prove that an export finished. Neither does a process that exits successfully after swallowing an exception. Decide what success means for the job, then send a heartbeat at that boundary.
For a nightly backup, the boundary might include creating the artifact and verifying that the remote destination accepted it. For a report, it might include producing the intended output. If the scheduler merely queues work, a ping immediately after dispatch confirms dispatch, not completion by a worker.
DeployWatchers uses a last-received heartbeat with an expected interval and grace period. Its heartbeat URL accepts a success signal. Do not append start or failure suffixes copied from a different monitoring service unless the receiving service documents support for them.
Preserve the job's failure status
Consider a Unix shell wrapper in which the real task and the notification are separate steps. The following pattern runs on the job host; replace the task path and install a protected curl configuration file before using it.
#!/bin/sh
if /opt/jobs/create-report.sh; then
curl --config /etc/monitoring/report-heartbeat.curl \
--fail --silent --show-error \
--connect-timeout 5 --max-time 10 --output /dev/null
else
job_status=$?
exit "$job_status"
fi
The configuration file contains the full secret-bearing heartbeat URL:
url = "https://monitor.example/api/v1/heartbeats/REPLACE_WITH_SECRET_TOKEN"
Create it through your secret-management procedure, restrict it to the job account and never commit a real token. Keeping the URL in a protected file avoids placing it directly in the curl process arguments. Protect proxy and server access logs too, because the receiver still sees the path. The curl manual (opens in a new tab) documents configuration-file and timeout options.
The wrapper intentionally does not follow redirects. A changed ping endpoint should be reviewed, rather than forwarding its secret-bearing request to an arbitrary destination.
Keep task failure and notification failure distinct
In a local fixture, a successful task produced one ping. A task exiting with status 7 produced no additional ping, and its status remained 7. When the task succeeded but the receiver returned 503, curl failed with status 22. That was a delivery failure, not evidence that the task itself failed.
The wrapper lets the notification failure make the overall invocation fail so the scheduler can log it. Configure retry behavior with that distinction in mind. Automatically rerunning a non-idempotent job because its final ping failed can duplicate an export, an email or a financial action.
If you need notification retries, retry the notification separately within a bounded policy. Check how the receiver treats duplicate pings and how retries affect the recorded completion time. A heartbeat tells you when the receiver heard from the process, not necessarily the original business completion timestamp.
Choose an interval from completion gaps, not wishful timing
For an interval-based check, think about the longest reasonable gap between successful completions. Suppose a task normally completes every hour and its duration varies by several minutes. A sixty-minute expectation with no allowance can mark a healthy run as late simply because this run took longer than the previous one.
In DeployWatchers, after a recorded ping, the job becomes late when the expected interval has elapsed. It becomes down after the interval plus grace. For a new check that has never pinged, the initial deadline is based on its creation time. Plan the first run accordingly instead of assuming monitoring begins only after the first success.
For example, with a last ping at 12:00, an interval of sixty minutes and ten minutes of grace, a missing ping is late after 13:00 and down after 13:10. Actual evaluation and notification can occur later because the monitor itself runs on a schedule.
Treat calendar schedules and intervals as different models
A job scheduled at a fixed wall-clock time is not always equivalent to a fixed duration between pings. Daylight-saving changes, skipped dates and maintenance windows can create a different completion gap. Keep the scheduler's timezone explicit and verify what your monitoring service actually supports.
For a complex calendar schedule, use a monitor that understands it or choose an interval and grace that honestly reflect the resulting gaps. Do not describe an interval-based check as if it independently evaluates your crontab expression.
Also budget for queue delay if the scheduled task dispatches a job. A healthy scheduler with a stalled worker is precisely the case a completion heartbeat should expose. Laravel's task scheduling documentation (opens in a new tab) describes the scheduler itself; the heartbeat remains an independent observation of the chosen completion boundary.
Test the missing-ping path before relying on it
Set up a disposable check and stop its pings deliberately. Verify that it becomes late and then down, and that the intended recipient receives an alert. Resume a successful run and verify recovery. Use a short test interval rather than waiting days to discover that notifications were misconfigured.
The heartbeat is also a dependency. If the job completes but DNS, TLS or the monitoring endpoint is unavailable, the monitor sees a missing signal. Investigate job output and ping delivery together before assigning a cause.
Finally, keep the success criterion under review. A perfectly regular heartbeat from a script that produces an empty report is reliable telemetry about the wrong event. Pair the signal with a small output sanity check where correctness matters, and make that check fail the task when the promised work did not happen.
Catch problems after your next deployment
Monitor availability, browser JavaScript errors and visual changes with DeployWatchers.