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.
Defaults to "*/5 * * * *". Five-field cron expression for the check run.
Defaults to 90. Raw results and daily summaries older than this are
deleted by the cleanup job.
Defaults to 90. Width of the uptime grid and the window the snapshot is
built from. Must not exceed retentionDays.
Defaults to "10 0 * * *". Compacts yesterday's raw results into daily
summaries.
Defaults to "0 3 * * *". Deletes history beyond retentionDays.
The three jobs
| Job | Default cron | Runtime call | What it does |
|---|---|---|---|
| checks | */5 * * * * | statusPage.cron.runChecks() | Runs monitors, stores results, raises events and incidents. |
| summarize | 10 0 * * * | statusPage.cron.summarize() | Compacts raw rows into one summary row per monitor per day. |
| cleanup | 0 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.
| Expression | Meaning |
|---|---|
* * * * * | every minute |
*/5 * * * * | every five minutes |
0,30 * * * * | on the hour and half past |
15 2 * * * | 02:15 UTC daily |
0 9 * * 1-5 | 09:00 UTC on weekdays |
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:
displayDaysis what a visitor sees. It is the width of the uptime grid and the range the snapshot query reads.retentionDaysis 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.
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.
0disables 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,cleanupand every admin mutation invalidate it. After an out-of-band write, callstatusPage.api.invalidate()yourself.
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
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.