Guide
8 min read

Cron monitoring is really automation failure visibility — get the signal before the stack

Operators ask for cron monitoring when what they want is to know that backups, syncs, and cleanups didn't silently fail. The fix starts with heartbeats, not a monitoring platform.

Cron monitoring is really automation failure visibility — get the signal before the stack

Open the r/selfhosted thread asking about cron monitoring. The first comments are about Healthchecks.io, ntfy, gotify. Look closer at the question. The operator isn't actually worried about cron itself. They're worried that their backups didn't run last night. That their sync job failed silently. That their cleanup didn't fire and the disk filled up. The cron is just where those jobs live.

The ask is for automation failure visibility. The right tools match that need — they answer the questions the operator is actually losing sleep over.

This guide walks through the visibility problem in order: what to monitor, how to monitor it without standing up a stack, and when to upgrade.

What "silent failure" actually looks like

Cron's design is famously quiet. A job that fails leaves an exit code; cron does nothing visible with it. If the job's own logs aren't being read, the operator finds out after something downstream breaks.

A few common silent failures:

  • The backup script ran every night for two months. The last week it's been exiting non-zero before the upload step. The restore drill on day 70 reveals the gap.
  • The cleanup job is supposed to delete files older than 30 days. A path was misconfigured; it's been deleting nothing for months. The disk is now full.
  • The sync job is supposed to push reports to S3 every morning. The credentials expired three weeks ago; the job has been quietly failing every morning since.
  • The cert renewal hook is supposed to reload nginx after a renewal. The reload hasn't been running; users are seeing expired certs.

In each case, cron worked perfectly. The job that ran inside it didn't. And the operator had no signal until something downstream failed.

This is why cron monitoring is really automation failure visibility. The point isn't to know that cron ran. The point is to know that the automation did what it was supposed to.

The minimum useful pattern: start + end pings

The cheapest pattern that gets you most of the visibility:

Each job pings a unique URL when it starts, and again when it finishes successfully. A central service knows the schedule. If a ping is missed, the service alerts.

#!/bin/sh
set -e
URL="https://hc-ping.com/<uuid>"
curl -fsS -m 5 --retry 3 "$URL/start"
./real-job.sh
curl -fsS -m 5 --retry 3 "$URL"

This pattern catches:

  • Non-runs. The cron didn't fire at all. The service notices the missing start ping.
  • Failures. The job started but exited non-zero. The service notices the missing finish ping.
  • Hangs. The job started but didn't finish before its deadline. The service notices the missing finish ping.

All three are common. None of them are visible without a heartbeat layer. With it, you find out within minutes of the deadline instead of weeks later.

What the central service has to do

The simplest service:

  • Knows the schedule of each job.
  • Knows the deadline for completion.
  • Tracks the most recent start and finish times.
  • Sends an alert (email, push, webhook) when a deadline is missed.

Healthchecks.io is the canonical hosted version. Self-hosted alternatives include healthchecks-io (the self-hostable open-source codebase), cron-mon, or a 50-line Python script with SQLite and a cron of its own that checks for missed deadlines.

The self-hosted route is appropriate if you have many jobs, want to avoid an external dependency for monitoring, or are running in a network without outbound HTTP.

Adding context to the failures

A bare missed-ping alert tells you something failed, not what. The next move is to make the failures explain themselves.

Capture exit codes. The wrapper script captures the exit code and posts it as part of the failure ping or as a separate event.

Capture last lines of output. When the job fails, the wrapper captures the last 50 lines of stderr and includes them in the alert.

Tag jobs. Each job has a stable name, a category ("backups", "sync", "cleanup"), and an owner. Alerts include the tags.

Differentiate severity. A failed backup is critical. A failed cleanup is a warning. The service should respect that distinction so on-call isn't paged for everything.

None of this requires a heavyweight stack. It's mostly discipline in the wrapper script and in how the central service is configured.

What goes in the wrapper script

A reasonable production wrapper for any cron job:

#!/bin/sh
set -eu
JOB="$1"
URL="https://hc-ping.com/${HC_TOKEN}/${JOB}"
LOG=$(mktemp)
trap "rm -f $LOG" EXIT

curl -fsS -m 5 "$URL/start" >/dev/null
if "$@" >"$LOG" 2>&1; then
    curl -fsS -m 5 --data-binary @"$LOG" "$URL" >/dev/null
else
    EXIT=$?
    tail -n 50 "$LOG" | curl -fsS -m 5 --data-binary @- "$URL/fail?code=$EXIT" >/dev/null
    exit $EXIT
fi

This wrapper:

  • Pings start.
  • Captures the job's stdout/stderr to a temp file.
  • On success, posts the log to the success URL.
  • On failure, posts the last 50 lines and the exit code to the fail URL.
  • Cleans up the temp file regardless.

Use it as cron-wrap backup-nightly ./backup.sh /data — every cron line follows the same pattern.

Categories worth monitoring carefully

Not every cron is equal. A few categories deserve extra-careful visibility:

Backups. A silent backup failure is a future restore failure. Always heartbeat. Always alert on failure.

Cleanups. A silent cleanup failure ends in a disk fill or a quota breach. Always heartbeat. Alert at warning severity.

Sync jobs. Silent sync failures mean data is missing somewhere downstream. Always heartbeat. Alert at warning severity, escalate after N missed runs.

Cert and credential renewals. Silent renewal failure means a user-visible outage when the cert or key expires. Always heartbeat. Alert at critical severity.

Report generation. Silent report failure usually shows up when somebody asks where the report is. Heartbeat. Alert at low severity.

The pattern is the same; the severity differs. Get the patterns in place; tune the severity later.

What to defer

A few things people mistakenly add too early.

Full monitoring stacks. Prometheus, alertmanager, exporters for cron metrics — overkill for the cron-monitoring problem. The heartbeat layer answers the question.

Distributed scheduling. Airflow, Argo, Temporal — useful when jobs are complex DAGs with dependencies. For "the nightly backup runs at 3am," plain cron + heartbeats is fine.

Custom dashboards. Most operators look at the dashboard zero times per week as long as the alerts are working. Don't build a dashboard until you find yourself wanting one.

Cron clustering. Useful if you really do need fault tolerance for the scheduler itself. Almost nobody does.

Defer all four. Heartbeats are the load-bearing infrastructure.

When the heartbeat layer isn't enough

A few signals tell you it's time to upgrade:

  • You have dozens of related jobs whose dependencies matter.
  • You need to retry failed jobs automatically with backoff.
  • You need cross-job audit ("which jobs touched this customer in the last week?").
  • You have an SLA that includes job freshness.
  • You're running on behalf of customers, not just yourself.

When those conditions hit, look at Cronicle, then at a proper scheduler. Until then, heartbeats are doing the work.

The cultural shift

The biggest gain from heartbeating every job isn't technical. It's cultural. The team stops treating "cron didn't fire" as a thought-experiment and starts treating it as a known monitored condition.

A new job, by team convention, ships with a heartbeat or doesn't ship. The next operator coming into the codebase sees the wrapper and knows the discipline. The thing that used to be a quiet failure mode becomes a visible one. Over months, the system gets less spooky.

The summary

When people ask about cron monitoring, they want automation failure visibility. The right pattern is heartbeats — start and finish pings to a central service that alerts on missed deadlines. Wrap every job. Capture exit codes and log tails. Tag and categorize. Defer the heavy stacks. The result is that silent failures stop being a category that exists. You find out within minutes of the deadline; you don't find out via the downstream consequence.

cron
scheduled-tasks
monitoring
alerts
backups
automation

From across the StoicSoft network

Hand-curated reads on the same topic from sister sites in the StoicSoft family.

Keep reading

Guide

Self-hosted cron and backup jobs need dead-man monitoring before silent failures become data loss

Scheduled jobs that stop running silently are more dangerous than services that crash loudly. Here is how to build dead-man monitoring for your cron jobs, backups, and maintenance tasks before a missed run turns into actual data loss.

Guide
8 min

Container monitoring is moving from uptime dashboards to egress policy, backup-state widgets, and service-level proof

Fresh self-hosting posts point to a more specific monitoring need than simple green/red uptime. One homelab user running rootless Podman quadlet services wants per-container outbound blocking, domain allowlists, and traffic visibility but cannot see how to…

Guide
7 min

Beyond Uptime: Monitoring the Real World — UPS, Floods, and Camera Streams

Your homelab can watch more than its own services. People use it to catch basement floods, UPS failures, and front-door events. How to add real-world monitoring without overbuilding a NOC.

Guide
5 min

Maintenance windows done right: mute the noise, not the real outages

Planned upgrades shouldn't spam your alerts. But broad muting hides real breakage. Here's how to set maintenance windows that suppress expected noise while keeping genuine incidents visible.

Guide
11 min

Self-Hosted n8n: The Production-Readiness Checklist the Install Guide Skips

You followed the n8n install guide. The container is running. Production starts ten steps later — backups, restore drills, metrics, version pinning, workflow git history. The five gaps that turn a toy setup into a real one.

Guide

Homelab hardware monitoring needs host-fault alerts before services start crash-looping

When a homelab operator lost two CPU cores, the first sign wasn't a hardware alert — it was Nextcloud crashing in a loop. This post walks through the layers of monitoring you need to catch host-level faults before your services become the canary.