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.
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.
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.
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.
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.
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:
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
// 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 });
}
In Vercel Dashboard → Settings → Environment Variables, add:
CRONWATCH_URL = https://agent-control-plane-w3m9.onrender.com/api/cronwatch/heartbeat/abc123xyz
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.
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.
Heartbeat monitoring answers the most basic question: did the job complete? But for production cron jobs, you often want to know more:
console.log(JSON.stringify({ records_processed: n, errors: 0 })). If you're draining logs to a service, you can alert on records_processed: 0 as an anomaly.Date.now() before and after your logic. Log it or include it in the heartbeat payload. Sudden spikes indicate upstream slowdowns.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.
When CronWatch fires an alert (or you notice missed runs), start with Vercel's invocation logs:
Common fixes:
CRON_SECRET in your env matches vercel.json's crons auth setupvercel.json is valid and the deployment succeeded| 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 |
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