Templates / Uptime check
An uptime check that checks something
Most uptime monitoring asks one question: did the server answer? That misses the state that actually costs you customers — the process is up, it returns 200, and the database behind it has been unreachable for ten minutes.
The recipe
- Request
- GET https://{{domain}}/health
- Schedule
- Every minute (* * * * *)
- Timeout
- 10s
- Attempts
- 3
- Counts as success
- 2xx and body contains ok
You supply
- Your domain
- example.com
Host only — no https:// and no trailing slash.
Why this job exists
A plain ping tests the thinnest possible slice of a system: that a socket accepted a connection and something wrote a status line. Every layer that matters sits behind that. An application whose database connection pool is exhausted usually still serves its health route. One whose disk is full often does too. One whose upstream payment provider is down certainly does.
The gap between "responding" and "working" is where the expensive outages live, because monitoring says green throughout and the first report comes from a customer. By the time someone checks, the incident is however long it took a human to notice and complain.
Closing the gap needs the health endpoint to assert something, and the check to read what it said. Three assertions together — status, body, latency — cover most of what a single request can tell you.
Setting it up
Write a health route that checks its dependencies and reports them, rather than returning a constant. The status code should reflect the verdict so that both a status check and a body check agree:
A health route with an opinion
export async function GET() {
const checks = {
database: await ping(db),
cache: await ping(redis),
queueDepth: await queue.size(),
};
const healthy =
checks.database && checks.cache && checks.queueDepth < 10_000;
return Response.json(
{ status: healthy ? 'ok' : 'degraded', ...checks },
{ status: healthy ? 200 : 503 },
);
}This recipe asserts the body contains "ok" and gives the request ten seconds. Both matter: the body check catches a degraded response that still returns 200, and the short timeout catches the state where everything works but slowly, which is what an overloaded system looks like just before it stops working entirely.
Checking it actually works
- Break a dependency deliberately in staging — stop the cache, revoke a credential — and confirm the check goes red rather than staying green.
- Check that the health route is not itself cached. A CDN serving a cached "ok" makes this monitoring the cache.
- Look at the latency trend, not just the status. Response time creeping up over a week is the warning that a status column never shows.
Where it goes wrong
A health route that returns a constant
Returning a hard-coded "ok" tests whether the web server is running and nothing else. If the route does not touch its dependencies, this job cannot tell you anything a ping could not.
Health checks that are expensive
A route that runs a full diagnostic every minute becomes load in its own right, and a public one becomes a denial-of-service amplifier. Keep the checks cheap, and cache the expensive parts internally for a few seconds.
Alerting on a single failure
One failed request is usually a network blip. The failure threshold here defaults to alerting on the first failure, which is right for a minute-by-minute check on something critical and too noisy for a flaky third-party dependency — raise it where you would not act on a single miss.
Questions
Should the health endpoint be public?
It should be reachable without a session, but it should not expose internals. Report a verdict per dependency, not connection strings, versions or stack traces.
What should the timeout be?
Well under the cadence, and near the latency at which you would consider the service unusable. Ten seconds is generous for a health route; if yours needs more than that, the answer is already "degraded".
Is this a replacement for a real observability stack?
No. This is black-box monitoring — it tells you the front door works from outside. It complements metrics and tracing rather than substituting for them, and it has the advantage of failing when your telemetry pipeline does.
Running it here
This is a preset in the product, not an illustration. Pick it in the dashboard, fill in the one value above, and the job is created, scheduled and enabled — with retries, a record of every run showing status, latency and response, and an alert the first time one fails. Secret values are encrypted at rest and never rendered back.
The free plan runs 3 jobs hourly and needs no card. Where a recipe wants a finer cadence than your plan allows, it is slowed to the fastest schedule you are permitted rather than rejected.