Runbook for API Failures and Silent Cron Jobs in a Backend Metrics Dashboard
Use metrics APIs for cron-job, API-failure, and business-event charts, then add a separate heartbeat monitor for jobs that never start. That is the smallest stack I would put on call for a small SaaS. A metrics dashboard can show success and failure counts, duration, backlog size, and error-rate trends; it cannot prove that a scheduler actually invoked a job. Healthchecks-style monitoring closes that specific gap. It still isn't full monitoring coverage, and I wouldn't describe it that way in an SLO review. The distinction matters because a failed run and a missing run leave different evidence. An API error usually increments something. A business event can be counted. A cron job that never fires may produce nothing at all — no duration, no failure, no final log line. No signal. How should a backend metrics dashboard combine cron jobs, API failures, and healthchecks? Start with the questions an operator must answer, not with a vendor menu. For cron jobs, I want a success count, a failure count, duration, and any queue backlog that can delay completion. For API failures, I want error counts and an error-rate trend beside request volume, because a raw count without a denominator can make ordinary traffic growth look like a regression. For business events, I want domain verbs: invoices issued, imports completed, or messages accepted. Those widgets belong on one dashboard because they describe the same service from different angles. Heartbeat monitoring is a separate control. A job reports a start or completion ping to Healthchecks, Cronitor, or an equivalent tool; if the expected ping doesn't arrive within its schedule and grace period, that system owns the missing-run signal. Keep that alert outside the metrics query path. Otherwise the component that failed to emit data is also the component being asked to notice its own silence. Silence counts. I've learned to write the failure matrix before drawing the dashboard. In one incident, a call returned 200, but the side e