← Blog

Cron job heartbeat monitoring for indie SaaS

Cron jobs and background queues fail quietly: the web app stays green while nightly billing or email sync stopped three days ago. A heartbeat pattern means the job calls an HTTP endpoint after a successful run; silence past the expected window means something broke.

StillOnline runs scheduled HTTP GET checks on URLs you register — not a passive “missed ping” clock like Healthchecks.io. This guide shows how to adapt the heartbeat pattern with an HTTP health URL, when that is enough on Free (one URL, five-minute probes per pricing), and when to pair StillOnline with a cron-specific tool.

Quick answer

StillOnline does not ship a native “cron missed ping” monitor. Use an HTTP URL the job hits after success — for example GET /internal/cron-heartbeat returning 200 with a timestamp — and register that URL in StillOnline, or fold cron success into your main /health response. Probes run every five minutes on Free; two consecutive failures mark the check DOWN and trigger owner alerts. For sub-minute “job must run every hour” semantics, add a dedicated cron heartbeat SaaS or encode last-run time in a URL StillOnline already polls.

Heartbeat vs HTTP uptime vs CI checks

PatternWhat failsStillOnline fit
HTTP uptimeWeb/API unreachablePrimary — GET /health every 5 min
HeartbeatJob stopped running but server is upIndirect — job must update a URL or health JSON
CI-only checkDeploy broke; prod might still serve 200GitHub Actions ≠ production cron — API checks

Cron on Linux fires on schedule but does not tell you the script exited 0. Heartbeat closes that gap.

Pattern A — Dedicated heartbeat URL

  1. Add GET /internal/cron-heartbeat (auth at edge or secret query param — never expose secrets in public docs).
  2. On successful job completion, touch a file or DB row; the route returns 200 only if last_success is within your SLA window (e.g. 26 hours for daily cron).
  3. Register the HTTPS URL in StillOnline → HTTP check → expected 200.
curl -sS https://api.yourproduct.com/internal/cron-heartbeat

If the job fails, the route returns 503 or stale data — StillOnline goes red after two failed probes (~10 minutes on default interval).

Pattern B — Embed last-run in /health

Indie teams on Free (one URL) often add "cron_last_ok": "2026-06-08T06:00:00Z" to existing /health JSON and alert when parsing shows staleness — StillOnline still only checks HTTP status unless you return 503 when stale. Simpler: health handler returns 503 when now - last_run > threshold.

See health endpoint design and API-only monitoring.

Pattern C — Queue workers without a public port

Internal workers with no ingress cannot receive StillOnline GET directly. Options:

  • Sidecar HTTP on the worker process (:8081/health behind firewall is not probeable — you need a public URL).
  • Proxy metric: worker writes heartbeat to Redis/DB; your public /health reads it.
  • Separate component on the status page for “Background jobs” when web is up but queue is not — workers guide.

StillOnline setup

  1. Start free → new project.
  2. Add HTTP check on heartbeat or /health URL.
  3. Status page + Telegram owner alerts on DOWN.

Pro ($9/mo) adds up to 10 URL checks per project — split web vs cron heartbeat if needed. See pricing.

Related guides

FAQ

Does StillOnline support Healthchecks-style passive heartbeat URLs?

No. StillOnline actively GETs URLs on a schedule. Your cron must keep a reachable HTTPS endpoint in a success state, or your /health must flip to non-200 when jobs stall.

Can GitHub Actions replace StillOnline for cron monitoring?

Actions prove the workflow ran in CI; they do not replace external probes of production URLs customers use. Use both for different layers — GitHub Actions vs production.

How fast does StillOnline alert when a daily cron misses one run?

Default five-minute interval and two consecutive failures mean roughly 10 minutes after the URL stays unhealthy — not instant. Tune your heartbeat route to return 503 as soon as the SLA window passes.