1. Documentation
  2. Configuration
  3. Checks and scheduling
ReadmenpmGitHub
  • Introduction
  • Quickstart
  • Configuration file
  • Monitors
  • Checks and scheduling
  • Notifications
  • Incidents and maintenance
  • Subscribers
  • Metrics and badges
  • Identity and appearance
  • Telemetry
  • The check run
  • Uptime and history
  • Automatic incidents
  • Notification delivery
  • Public subscriptions
  • StatusPage component
  • Themes
  • UI primitives
  • Colour mode
  • React Router
  • Next.js
  • Astro
  • SQLite
  • Cloudflare D1
  • PostgreSQL
  • Schema and migrations
  • Cloudflare Workers
  • Vercel
  • Self-hosting
  • API overview
  • Status endpoints
  • Incident and maintenance endpoints
  • Subscriber endpoints
  • Badges, feeds and metrics
  • Admin endpoints
  • CLI commands
  • Package exports
  • Security
  • Troubleshooting

Checks and scheduling

Intervals, retention, the displayed window and the snapshot cache.

checks describes when monitors run and how much history survives. The same three cron expressions drive the local dev scheduler, Cloudflare triggers, Vercel Cron and node-cron, so a schedule is declared once and honoured everywhere.

ts
checks: {  interval: "*/5 * * * *",  retentionDays: 90,  displayDays: 90,  summaryCron: "10 0 * * *",  cleanupCron: "0 3 * * *",},
intervalstringoptional

Defaults to "*/5 * * * *". Five-field cron expression for the check run.

retentionDaysnumberoptional

Defaults to 90. Raw results and daily summaries older than this are deleted by the cleanup job.

displayDaysnumberoptional

Defaults to 90. Width of the uptime grid and the window the snapshot is built from. Must not exceed retentionDays.

summaryCronstringoptional

Defaults to "10 0 * * *". Compacts yesterday's raw results into daily summaries.

cleanupCronstringoptional

Defaults to "0 3 * * *". Deletes history beyond retentionDays.

The three jobs

JobDefault cronRuntime callWhat it does
checks*/5 * * * *statusPage.cron.runChecks()Runs monitors, stores results, raises events and incidents.
summarize10 0 * * *statusPage.cron.summarize()Compacts raw rows into one summary row per monitor per day.
cleanup0 3 * * *statusPage.cron.cleanup()Deletes results and summaries older than retentionDays.

Each job invalidates the snapshot cache when it finishes, so the next page render reflects it.

Cron syntax

Heyo Status evaluates standard five-field expressions — minute, hour, day-of-month, month, day-of-week — in UTC. Supported field syntax is *, a number, a a-b range, a , list and a /n step. Sunday may be 0 or 7. When both day-of-month and day-of-week are explicit, they are combined with OR, as in classic cron.

ExpressionMeaning
* * * * *every minute
*/5 * * * *every five minutes
0,30 * * * *on the hour and half past
15 2 * * *02:15 UTC daily
0 9 * * 1-509:00 UTC on weekdays
Platform minimums apply

Cloudflare and Vercel schedule cron on their own cadence and will not run a trigger more often than their plan allows. A one-minute interval is a local development convenience, not a guarantee in production.

Retention and the displayed window

Two numbers, two different jobs:

  • displayDays is what a visitor sees. It is the width of the uptime grid and the range the snapshot query reads.
  • retentionDays is what the database keeps. Raw rows older than it are deleted, as are daily summaries older than the same boundary.

Validation rejects displayDays > retentionDays because the page would ask for days the cleanup job already removed.

ts
// A 30-day page backed by a year of history for internal analysis.checks: { displayDays: 30, retentionDays: 365 }

Storage grows with monitors × checks per day, until compaction turns a day into one row per monitor. At a five-minute interval that is 288 raw rows per monitor per day, collapsing to one summary row.

Snapshot cache

Status pages are read-heavy and each snapshot is derived from the whole displayed window, so the runtime keeps one in-process snapshot and serves it for a short time.

ts
cache: { snapshotTtlMs: 30_000 }
  • 0 disables caching entirely; every read hits the database.
  • The maximum accepted value is 3600000 (one hour).
  • Concurrent misses are collapsed into a single database read.
  • Passing an explicit clock — getSnapshot(new Date(...)) — always bypasses the cache, which keeps tests deterministic.
  • runChecks, summarize, cleanup and every admin mutation invalidate it. After an out-of-band write, call statusPage.api.invalidate() yourself.
Serverless caches are per-instance

On Cloudflare Workers and Vercel functions the cache lives inside one isolate or instance. It still removes most repeated reads inside a burst, but it is not a shared cache and does not need to be invalidated across instances.

Running the schedule

# Runs the framework dev server and all three jobs.heyo-status dev -- next dev

The platform configuration must match the configuration file. On Cloudflare, cloudflareCronExpressions(statusPage) returns exactly the list wrangler needs, and the Worker handler matches an incoming trigger against the three expressions — an unknown trigger is deliberately a no-op, so stale cron configuration during a deploy is harmless.

Related

  • How history is compacted
  • Deploying to Cloudflare
  • CLI commands
Monitors< PreviousNotificationsNext >

Powered by heyo

On this page

The three jobsCron syntaxRetention and the displayed windowSnapshot cacheRunning the schedule