tutorial

Monitoring Vercel Edge Functions with Vigilmon: Health Routes, Cron Heartbeats & Status Page Embeds

Monitor Vercel Edge and Serverless function routes with Vigilmon — health API routes, Vercel Cron heartbeats, status page embeds, and alert channels for production Vercel apps.

Vercel deploys fast and scales automatically, but that doesn't make it failure-proof. An environment variable missing from your production project, an Edge Function that silently rejects requests in one geo, a Cron Job that stopped firing after a quota limit — these failures are invisible until a user complains. Vigilmon gives you the external health checks, heartbeat monitors, and status page that Vercel's own dashboard doesn't provide.

This tutorial wires a Vercel-deployed app into Vigilmon end-to-end.

What You'll Build

  • An /api/health route running in the Edge Runtime
  • Vigilmon HTTP monitors for both Edge Function and Serverless function routes
  • A Vercel Cron Job that pings a Vigilmon heartbeat on each successful run
  • A public status page embeddable in your app or README

Prerequisites

  • A Vercel-deployed Next.js app (App Router recommended; Pages Router works too)
  • Vercel Pro plan or above for Cron Jobs (Hobby plan has limited cron support)
  • A free account at vigilmon.online

Step 1: Create the Edge Runtime Health Route

Edge Functions run on Vercel's global network using the V8 isolate runtime — no Node.js APIs. Your health check must use only Web-standard APIs.

App Router — app/api/health/route.ts

export const runtime = "edge";
export const dynamic = "force-dynamic";

export async function GET() {
  const checks: Record<string, string> = {};
  let ok = true;

  // Check a downstream HTTP dependency
  try {
    const resp = await fetch(
      process.env.DATABASE_HEALTH_URL ?? "https://example.com/ping",
      { signal: AbortSignal.timeout(3000) }
    );
    checks.database = resp.ok ? "ok" : `http_${resp.status}`;
    if (!resp.ok) ok = false;
  } catch (err) {
    checks.database = `error: ${(err as Error).message}`;
    ok = false;
  }

  // Check a second upstream (e.g. your auth service)
  try {
    const resp = await fetch(
      process.env.AUTH_SERVICE_URL + "/health",
      { signal: AbortSignal.timeout(2000) }
    );
    checks.auth = resp.ok ? "ok" : `http_${resp.status}`;
    if (!resp.ok) ok = false;
  } catch (err) {
    checks.auth = `error: ${(err as Error).message}`;
    ok = false;
  }

  return Response.json(
    { status: ok ? "ok" : "degraded", runtime: "edge", checks },
    { status: ok ? 200 : 503 }
  );
}

Pages Router — pages/api/health.ts

If you're on Pages Router and want the Edge Runtime:

import type { NextRequest } from "next/server";

export const config = { runtime: "edge" };

export default async function handler(req: NextRequest) {
  const checks: Record<string, string> = {};
  let ok = true;

  try {
    const resp = await fetch(process.env.DATABASE_HEALTH_URL!, {
      signal: AbortSignal.timeout(3000),
    });
    checks.database = resp.ok ? "ok" : `http_${resp.status}`;
    if (!resp.ok) ok = false;
  } catch (err) {
    checks.database = `error: ${(err as Error).message}`;
    ok = false;
  }

  return Response.json(
    { status: ok ? "ok" : "degraded", checks },
    { status: ok ? 200 : 503 }
  );
}

Deploy and verify locally:

curl https://<your-app>.vercel.app/api/health
# {"status":"ok","runtime":"edge","checks":{"database":"ok","auth":"ok"}}

Step 2: Monitor a Serverless Function Route

Not all Vercel functions run at the edge. API routes that use Node.js APIs (file system, native modules, Prisma with the default engine) run as Serverless Functions. Monitor them separately so you detect function-runtime-specific failures.

Create a Serverless Function health route alongside your Edge one:

// app/api/health-node/route.ts
// No `export const runtime = "edge"` — defaults to Node.js runtime

import { NextResponse } from "next/server";
import { db } from "@/lib/db"; // Prisma or similar

export const dynamic = "force-dynamic";

export async function GET() {
  const checks: Record<string, string> = {};
  let ok = true;

  try {
    await db.$queryRaw`SELECT 1`;
    checks.database = "ok";
  } catch (err) {
    checks.database = `error: ${(err as Error).message}`;
    ok = false;
  }

  return NextResponse.json(
    { status: ok ? "ok" : "degraded", runtime: "nodejs", checks },
    { status: ok ? 200 : 503 }
  );
}

Step 3: Add Vigilmon HTTP Monitors

Add one Vigilmon monitor per route runtime so you get independent alerts:

Edge Function monitor

  1. Log in to VigilmonAdd Monitor → HTTP.
  2. URL: https://<your-app>.vercel.app/api/health
  3. Check interval: 60 seconds
  4. Response timeout: 5 seconds (Edge Functions warm instantly — no cold-start budget needed)
  5. JSON assertion: status = ok
  6. Label: "Edge health — production"

Serverless Function monitor

Repeat with:

  • URL: https://<your-app>.vercel.app/api/health-node
  • Response timeout: 10 seconds (Serverless Functions can cold-start)
  • Label: "Serverless health — production"

Step 4: Vercel Cron Job Heartbeat

Vercel Cron Jobs run your API routes on a schedule. If the cron silently stops (quota exceeded, deployment regression, runtime error), you won't know without an external heartbeat monitor.

vercel.json

{
  "crons": [
    {
      "path": "/api/cron/daily-report",
      "schedule": "0 6 * * *"
    }
  ]
}

app/api/cron/daily-report/route.ts

export const dynamic = "force-dynamic";

export async function GET(request: Request) {
  // Vercel adds this header to cron requests; check it in production
  const authHeader = request.headers.get("authorization");
  if (
    process.env.NODE_ENV === "production" &&
    authHeader !== `Bearer ${process.env.CRON_SECRET}`
  ) {
    return new Response("Unauthorized", { status: 401 });
  }

  try {
    await generateDailyReport(); // your actual job

    // Ping Vigilmon heartbeat on success
    await fetch(process.env.VIGILMON_HEARTBEAT_URL!, { method: "POST" });

    return Response.json({ ok: true });
  } catch (err) {
    // Do NOT ping heartbeat — let Vigilmon fire the alert
    console.error("Daily report failed:", err);
    return Response.json({ ok: false, error: (err as Error).message }, { status: 500 });
  }
}

Add to your Vercel project environment variables:

VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/<your-heartbeat-id>
CRON_SECRET=<random-secret>

In Vigilmon, create a Heartbeat monitor and set the grace period to 30 hours (for a daily job) so a single missed run fires an alert.


Step 5: Embed the Status Page

Vigilmon generates a public status page for all your monitors. Go to Status Pages → Create and add your Vercel monitors. Then embed the badge in your README.md:

[![Uptime](https://vigilmon.online/badge/<monitor-id>.svg)](https://vigilmon.online/status/<status-page-slug>)

Or embed the live widget in your app's footer:

<script
  src="https://vigilmon.online/embed.js"
  data-page="<status-page-slug>"
  data-theme="light"
  async
></script>

What You Monitor vs. What Vercel Shows You

| Failure mode | Vercel dashboard | Vigilmon | |---|---|---| | Edge Function returning 5xx | Function logs (manual check) | HTTP monitor alerts immediately | | Serverless Function cold-start timeout | Invocation duration metrics | Timeout alert fires | | Cron Job stops running | Cron log (manual check) | Heartbeat grace period fires alert | | Missing environment variable causes crash | Deploy log doesn't warn | HTTP monitor catches broken route | | Status page for your users | Not provided | Public Vigilmon status page |


Alert Channel Recommendations

  • Slack webhook — post to your #incidents channel with the monitor name and status
  • Email — on-call address for P0 alerts
  • PagerDuty webhook — for high-severity production monitors

Set alert escalation to re-notify every 5 minutes while the monitor is down so the alert doesn't get buried.


Vercel handles deployments. Vigilmon handles the question "is it actually working?" — from the outside, the way your users experience it.

Monitor your Vercel app with Vigilmon today — register free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →