← Blog  ·  October 2026

Vercel Cron Job Monitoring: Get Alerted When Scheduled Functions Fail

Vercel cron jobs fail quietly. Here's how to make sure yours never stops running unnoticed.

Vercel Cron Jobs make it easy to run scheduled serverless functions — clean syntax in vercel.json, no server to manage, and it integrates natively with your Next.js or other Vercel-deployed app. But there's a catch that many developers only discover the hard way: Vercel cron jobs fail silently by default.

If your scheduled function throws an error, times out, or simply stops being invoked, you won't get a notification. The Vercel dashboard shows invocation logs, but only if you go looking. There's no built-in alerting for missed runs or consecutive failures. For a background job that's supposed to sync data, send emails, or process a queue — that silence can mean days of invisible breakage.

This guide covers the specific failure modes of Vercel Cron Jobs and how to add real monitoring without adding infrastructure complexity.


How Vercel Cron Jobs work (and where they break)

Vercel Cron Jobs are defined in your vercel.json:

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

At the scheduled time, Vercel sends an HTTP GET request to that route. The function runs as a normal serverless function — same execution limits, same cold start behavior. If the function responds with 2xx, the invocation is considered successful. If it times out or returns an error status, Vercel logs it — but doesn't alert you.

The failure modes that will catch you off guard:

The last one is particularly insidious. Vercel will tell you the cron ran. It won't tell you whether it actually did anything useful.


Option 1: Heartbeat monitoring (most reliable)

The most robust approach is a dead man's switch: your cron function pings an external endpoint on every successful run. If the ping doesn't arrive within a defined window, the monitoring service sends an alert.

Add a heartbeat ping at the end of your cron function, after all meaningful work is done:

// app/api/cron/daily-summary/route.ts (Next.js App Router)
export async function GET(request: Request) {
  // Verify the request came from Vercel's cron scheduler
  const authHeader = request.headers.get('authorization');
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return new Response('Unauthorized', { status: 401 });
  }

  // Your actual cron logic here
  await runDailySummary();

  // Heartbeat — only fires if the above succeeded
  await fetch(process.env.CRONWATCH_HEARTBEAT_URL!, { method: 'GET' });

  return Response.json({ ok: true });
}

The key detail: the heartbeat fires after runDailySummary(). If that function throws or hangs, the ping never happens, and the monitor alerts. If your cron job is silently skipped entirely, the ping never happens either.

Don't ping before your logic runs. Some developers put the heartbeat first "to make sure the monitoring knows the job started." That defeats the purpose — you want to know the job completed, not started. Put the heartbeat at the end.

Option 2: Vercel Log Drains

Vercel Pro and Enterprise plans support Log Drains — you can stream all function logs to a destination like Datadog, Logtail, or a custom endpoint. This gives you a complete record of every cron invocation and lets you set alerts on error patterns.

The setup: in your Vercel project settings, under Integrations, add a Log Drain pointed at your logging service. Then configure an alert for patterns like Error or a missing log line from your cron handler.

Downsides: this requires a Vercel Pro plan ($20+/month), a log aggregation service, and some configuration effort. For a single cron job, it's significant overhead. For a production application with many functions, it's worth it anyway.

Option 3: Vercel's native invocation logs (manual)

In your Vercel project dashboard under Functions, you can view recent invocations for each route. You'll see status codes, duration, and logs for each run. This is useful for debugging but not for alerting — it requires you to check manually.

If your cron job is low-stakes and you check your dashboard regularly, this might be enough. For anything business-critical, you want automated alerts.


Setting up a Vercel cron monitor with CronWatch

CronWatch is a dead man's switch service for scheduled jobs. You add one line to your cron function; if the heartbeat stops arriving, CronWatch emails you.

Here's the complete setup for a Vercel Cron Job:

Step 1: Get your heartbeat URL

Sign up for CronWatch at cronwatch-waitlist. You'll receive a unique heartbeat URL that looks like:

https://agent-control-plane-w3m9.onrender.com/api/cronwatch/heartbeat/abc123xyz

Step 2: Add the heartbeat to your function

// app/api/cron/[handler]/route.ts
export async function GET(request: Request) {
  try {
    // your cron logic
    await yourCronWork();

    // ping on success
    fetch(process.env.CRONWATCH_URL!, {
      method: 'GET',
      signal: AbortSignal.timeout(5000), // don't block response
    }).catch(() => {}); // fire-and-forget, don't fail the job
  } catch (err) {
    console.error('Cron failed:', err);
    throw err; // let Vercel log the error
  }

  return Response.json({ ok: true });
}

Step 3: Add the env variable

In Vercel Dashboard → Settings → Environment Variables, add:

CRONWATCH_URL = https://agent-control-plane-w3m9.onrender.com/api/cronwatch/heartbeat/abc123xyz

Step 4: Set the grace period

In CronWatch, configure the expected interval and grace period. For a daily job (0 9 * * *), set the expected interval to 24 hours and the grace period to 60 minutes. You'll get alerted if the job doesn't check in within 25 hours.


Handling multiple Vercel cron jobs

If you have several cron routes, give each one its own heartbeat URL. This way you get specific alerts ("the weekly report cron failed") rather than generic ones. CronWatch lets you create multiple monitors, each with a separate heartbeat endpoint, name, and alert threshold.

A useful pattern for teams: name each monitor after the route path. /api/cron/daily-summary → monitor named "daily-summary". Makes it obvious which job triggered an alert without digging through logs.


What to monitor beyond just "did it run"

Heartbeat monitoring answers the most basic question: did the job complete? But for production cron jobs, you often want to know more:

For most indie developers running 1-3 Vercel cron jobs, heartbeat monitoring covers 95% of the failure surface. A cron job that silently stops running is the single most common failure mode, and it's the one that's hardest to catch without a dead man's switch.


Debugging a failed Vercel cron job

When CronWatch fires an alert (or you notice missed runs), start with Vercel's invocation logs:

  1. Go to your project → Functions tab → find the cron route
  2. Filter by date range — look for the missed invocation window
  3. Check the status code and logs for that invocation

Common fixes:

Tip: validate your cron expression before deploying. Vercel uses standard Unix cron syntax. Paste your expression into a free parser to confirm it fires when you expect. Try the free cron expression parser →

Quick comparison: Vercel cron monitoring options

Option Alerts on missed runs? Cost Setup effort
Manual log checks No — manual only Free None (but no alerting)
Vercel Log Drains + alerting service Yes (with config) Pro plan + log service High
Heartbeat monitor (CronWatch) Yes — automatic $9/mo One line of code

🟢 Monitor your Vercel cron jobs with CronWatch

Add one line to your cron function. CronWatch alerts you by email if the job stops running — whether it crashes, times out, or simply stops being scheduled. Supports any cron job: Vercel, AWS Lambda, GitHub Actions, plain crontab.

Join CronWatch Waitlist → Free Cron Expression Parser