Updated 25 October 2026 · By the Upcertain team
1. Add a health route that does real work
A static page proves the CDN answers. A health route proves the app can reach what it depends on. Keep it small, uncached and free of customer data:
// app/api/health/route.ts
import { db } from "@/lib/db";
export const dynamic = "force-dynamic";
export async function GET() {
try {
await db.query("select 1");
return Response.json({ status: "ok" }, { headers: { "cache-control": "no-store" } });
} catch {
return Response.json({ status: "error" }, { status: 503 });
}
}force-dynamic makes sure the route runs on every request. Next.js 14 and earlier rendered such GET handlers once at build time, which would make the check meaningless.
2. Monitor it
| Monitor | Target | Settings |
|---|---|---|
| Keyword | https://yourapp.com/api/health | Must contain "status":"ok", every 30 s or 1 min |
| HTTP | https://yourapp.com/ | Expected 200, every 1 min |
| Keyword | Your sign-in page | Must contain a label that only renders when it works |
Every HTTPS monitor also checks the certificate and warns before expiry, which covers the custom domain on your Vercel project. Monitor the production domain, not *.vercel.app preview URLs, which change with every deploy.
3. Heartbeats for Vercel cron jobs
Vercel runs scheduled functions from the crons list in vercel.json by calling a path in your app. Hourly schedules like the one below need a paid Vercel plan; on Hobby, crons run at most once a day. If the function throws, times out or the schedule is removed in a refactor, nothing tells you. Report in at the end of the handler:
// vercel.json
{ "crons": [{ "path": "/api/cron/sync", "schedule": "0 * * * *" }] }
// app/api/cron/sync/route.ts
export async function GET() {
await syncEverything();
await fetch(process.env.UPCERTAIN_HEARTBEAT_URL!, { signal: AbortSignal.timeout(10_000) });
return Response.json({ ok: true });
}Create a heartbeat monitor with a one-hour period and some grace, and put its URL in an environment variable. If the sync stops running or starts failing before the last line, the heartbeat stops and you hear about it.
4. Tune for serverless
- Cold starts: the first request after a quiet period can be slow. A 10 to 15 second timeout avoids false failures without hiding real hangs.
- Deploys: Vercel switches traffic to the new deployment when it is ready, so deploys rarely fail checks. If yours do, add a confirmation check.
- Protect the cron route: when
CRON_SECRETis set, Vercel sends it asAuthorization: Bearer <CRON_SECRET>; check it so only Vercel can trigger the job. The heartbeat call itself needs no secret.
5. Tell users
Put the app, the API and the scheduled work on a status page at status.yourapp.com. When something fails at 3 a.m., users find the answer there instead of in your inbox.