Heartbeats: scheduled-task monitoring

A heartbeat watches over a scheduled task: a cron job, a nightly backup, a queue worker's cleanup, a report generator. The idea is a dead-man's switch, instead of Quiet Guard asking your task whether it ran, your task tells Quiet Guard every time it runs (a "ping"). When the pings stop, you get alerted.

The problem heartbeats solve

Exception tracking catches code that fails loudly: something throws, you get a report. But the most treacherous failures are silent:

  • the cron entry was lost during a server migration,
  • the scheduler is misconfigured (schedule:run no longer invoked),
  • the task crashes so early that no exception handler ever sees it,
  • someone disabled the job "temporarily" three months ago.

In all of these cases nothing throws, so nothing is reported. Your nightly backup simply stops existing, and you discover it the day you need it. A heartbeat inverts the logic: silence itself becomes the alert.

Quick start

The preferred wiring is the scheduler macro, chained directly on the task in routes/console.php:

php
Schedule::command('backup:run')->daily()->monitorHeartbeat('nightly-backup');

The macro pings only when the task succeeds, a crashing task stays silent and therefore triggers the overdue alert. You can also ping manually from any code path:

bash
php artisan monitor:heartbeat nightly-backup

or over HTTP (any stack, not just Laravel):

bash
curl -X POST https://your-monitor.example/api/v1/heartbeats/nightly-backup \
  -H "Authorization: Bearer <project API key>"

Registration and arming

  • The first ping auto-registers the heartbeat on the project. At this point it is unarmed: pings are recorded, nothing ever alerts. This is deliberate: you wire the ping first, then decide what "late" means.
  • Arming happens in the dashboard: open the project's Heartbeats tab, edit the heartbeat and set its expected period (how often the task should ping, in minutes) plus a grace margin (extra tolerated delay: schedulers rarely fire at the exact second).
  • A heartbeat with no expected period stays a passive log of pings, forever harmless.

Statuses

StatusMeaning
WaitingRegistered (or re-armed) but no ping evaluated yet against the period.
HealthyThe last ping arrived within period + grace.
OverdueNo ping within period + grace, the alert has fired.

Every minute, the server checks each armed heartbeat: past last ping + period + grace, it flips to overdue and fires one alert per outage through the project's notification channels subscribed to the heartbeat.overdue event (mail, Slack or webhook, see Alerting). Alert-storm protection applies.

Recovery is silent by design: the next successful ping flips the heartbeat back to healthy without a notification. The alert told you the task is stuck; the dashboard shows it healed.

Choosing period and grace

  • Period = the task's schedule. Daily task → 1440. Hourly → 60.
  • Grace = how much lateness is normal for that task. A backup that takes 5–40 minutes deserves a generous grace (say 60); a ping-after-cleanup task can keep the default 5.
  • When in doubt, start generous. A false "overdue" at 3 a.m. teaches you to ignore the channel: the one thing an alert must never do.

Limits and details

  • Slugs are normalized to lowercase-and-dashes (nightly-backup); create them in the UI with the same charset or let the first ping create them.
  • A project holds at most 200 heartbeats: a leaked API key cannot flood the table.
  • Pings authenticate with the project API key, like every ingestion call (API reference).
  • Overdue heartbeats surface on the project overview (health stats) and count as activity in the weekly digest.

You are reading the Quiet Guard v1.0 documentation.