1. Documentation
  2. Configuration
  3. Monitors
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

Monitors

HTTP, SSL, TCP, DNS and composite checks, their options and defaults.

A monitor describes one thing to check. Every monitor shares a small set of public fields, and each type adds its own. Names identify a monitor everywhere — in history rows, incidents, badges, metrics and notifications — so they must be unique and are expensive to rename.

ts
monitors: [  {    name: "Website",    description: "Public marketing site.",    group: "Web",    type: "http",    url: "https://acme.com",  },],

Shared fields

namestringrequired

Unique, non-empty. Used as the history key, the badge path segment, the Prometheus label and the display name.

descriptionstringoptional

Secondary line shown under the monitor name on the page.

groupstringoptional

Public category, for example "Web" or "API". Monitors sharing a group are rendered together, and the group gets its own derived status.

privatebooleanoptional

Defaults to false. A private monitor is still checked and still triggers notifications and automatic incidents, but is omitted from public snapshots, badges, metrics and the page.

http

The common case: request a URL and assert the response.

ts
{  name: "API",  type: "http",  url: "https://api.acme.com/health",  method: "GET",  headers: { authorization: "Bearer ${process.env.HEALTH_TOKEN}" },  expectedStatus: [200, 204],  expectedResponseHeaders: { "content-type": "application/json" },  expectedBodyIncludes: '"ok":true',  expectedBodyExcludes: "maintenance",  expectedJson: { status: "ok", database: { reachable: true } },  timeoutMs: 10_000,  softFail: true,  softFailMs: 1_000,  followRedirects: true,}
urlstringrequired

HTTP(S) only.

methodstringoptional

Defaults to "GET".

headersRecord<string, string>optional

Request headers. Values are sent verbatim — keep secrets in environment variables.

expectedStatusnumber[]optional

Defaults to [200]. Any listed status counts as healthy.

expectedResponseHeadersRecord<string, string>optional

Exact-match response headers. A missing or different value fails the check and names the header in the message.

expectedBodyIncludesstringoptional

Substring that must appear in the body. Requesting the body is what makes a "200 that renders an error page" detectable.

expectedBodyExcludesstringoptional

Substring that must not appear in the body.

expectedJsonJsonObjectoptional

Deep partial match against the parsed JSON body. Nested objects are matched key by key; arrays must be deeply equal.

timeoutMsnumberoptional

Defaults to 10000. A timeout is reported as HTTP timed out after <n>ms.

softFailbooleanoptional

Defaults to false. When enabled, a first failure is retried once after softFailMs before the result is recorded as down.

softFailMsnumberoptional

Defaults to 500. Delay before the soft retry.

followRedirectsbooleanoptional

Defaults to true. Set false to assert the redirect itself, usually with expectedStatus: [301].

A soft retry that succeeds is still recorded as up, with …; recovered on soft retry appended to the message. Latency always measures the whole check, including the retry window, so a soft-failing monitor reports honestly slow numbers.

ssl

Opens a TLS connection and inspects the peer certificate.

ts
{ name: "TLS certificate", type: "ssl", host: "acme.com", port: 443, warnBeforeDays: 21 }
hoststringrequired

Hostname, also sent as SNI.

portnumberoptional

Defaults to 443.

warnBeforeDaysnumberoptional

Defaults to 14. A certificate expiring sooner fails the check and is flagged as expiring rather than down.

timeoutMsnumberoptional

Defaults to 10000.

The check fails when the certificate chain is not authorized, when the expiry date cannot be read, or when the remaining lifetime is below warnBeforeDays. Only the last case is flagged expiring, which is what turns the alert into an ssl.expiring event instead of monitor.down.

Not available on Cloudflare Workers

ssl needs node:tls. On Workers the check records TLS monitoring is unavailable in this runtime. and reports down. Run certificate checks from a Node or Bun host, or leave them out of a Workers deployment.

tcp

Opens a socket and treats a successful connection as healthy.

ts
{ name: "Database", type: "tcp", host: "db.internal", port: 5432, private: true }
hoststringrequired
portnumberrequired

1–65535.

timeoutMsnumberoptional

Defaults to 10000.

Like ssl, this needs a native runtime — node:net — and is unavailable on Cloudflare Workers. Internal hosts are usually paired with private: true so the hostname never reaches the public page.

dns

Resolves a record through Cloudflare DNS-over-HTTPS, so it works in every runtime including Workers.

ts
{ name: "DNS", type: "dns", host: "acme.com", recordType: "A" }
hoststringrequired
recordTypestringoptional

Defaults to "A". Any type the DoH resolver accepts, e.g. AAAA, MX, TXT, CNAME, NS.

timeoutMsnumberoptional

Defaults to 10000.

The check fails when the HTTP query fails, when the DNS response status is not 0, or when the answer section is empty.

composite

A composite monitor performs no I/O. It aggregates other monitors from the same check run, which makes it the right tool for "is the product usable", rather than "is this one endpoint reachable".

ts
{  name: "Platform",  description: "Everything needed to sign in and get work done.",  type: "composite",  monitors: ["Website", "Dashboard", "API"],  strategy: "all",}
monitorsstring[]required

Names of configured, non-composite monitors. Duplicates are rejected, and an unknown name fails configuration validation.

strategy"all" | "any" | "majority"optional

Defaults to "all".

StrategyHealthy when
allevery component is healthy
anyat least one component is healthy
majoritymore than half the components are healthy

A composite reports the highest latency among its components, and its message states the ratio, for example 2/3 components healthy (strategy: all). If a component produced no result at all, the composite fails and names it — a misconfiguration must never look like a healthy aggregate.

Grouping and visibility

ts
monitors: [  { name: "Website", group: "Web", type: "http", url: "https://acme.com" },  { name: "Dashboard", group: "Web", type: "http", url: "https://app.acme.com" },  { name: "Queue", type: "tcp", host: "queue.internal", port: 5672, private: true },],
  • Monitors with the same group are published as a MonitorGroup whose status is operational, degraded (some failing) or outage (all failing).
  • Monitors without a group are rendered on their own, after the groups.
  • private: true removes a monitor from the public snapshot entirely, so it never appears in /status, /monitors, badges, Prometheus or the page.

Choosing timeouts and intervals

A check run executes every non-composite monitor concurrently, then derives composites. The slowest monitor therefore bounds the run, not the sum. Keep timeoutMs comfortably below checks.interval so a stalled endpoint cannot overlap the next scheduled run.

Related

  • What happens during a check run
  • Scheduling and retention
  • Notification events
Configuration file< PreviousChecks and schedulingNext >

Powered by heyo

On this page

Shared fieldshttpssltcpdnscompositeGrouping and visibilityChoosing timeouts and intervals