GUIDE · DENO · FREE

Monitor Deno cron jobs: Deno.cron native API and built-in fetch, zero dependencies

Deno.cron is a first-party cron API — stable in Deno 2.x. Wrap the handler in try/catch: await fetch(PING) on success; await fetch(PING + '/fail', ...) with the error body on failure; re-throw so Deno marks the run as failed. Built-in fetch, no npm, no dependencies. Absence detection covers the process that never starts or is killed. Free, no monitor cap.

The snippets

Create a monitor in the console, set the schedule plus a grace window, copy the ping URL, and add it to your script.

Deno.cron — basic try/catch (Deno 2.x)

Deno.cron accepts a name, a cron expression, and an async handler. Re-throw after the /fail ping so Deno can apply its own failure handling:

// job.ts — Deno 2.x (Deno.cron is stable, no flags needed) const PING = Deno.env.get("LASTPING_URL") ?? "https://ping.lastping.dev/<your-monitor-id>"; async function runJob(): Promise<void> { // your job } Deno.cron("nightly-etl", "0 2 * * *", async () => { try { await runJob(); await fetch(PING); } catch (err) { const msg = String(err instanceof Error ? err.stack : err); await fetch(`${PING}/fail`, { method: "POST", body: msg.slice(0, 10_000), headers: { "Content-Type": "text/plain" }, }); throw err; // re-throw so Deno marks the run as failed } });

With /start ping for overrun detection

A /start ping arms the hung-job alarm. A rid pairs start with finish so each run's duration is recorded in the event trail:

Deno.cron("nightly-etl", "0 2 * * *", async () => { const rid = Date.now().toString(); await fetch(`${PING}/start?rid=${rid}`); try { await runJob(); await fetch(`${PING}?rid=${rid}`); } catch (err) { const msg = String(err instanceof Error ? err.stack : err); await fetch(`${PING}/fail`, { method: "POST", body: msg.slice(0, 10_000), }); throw err; } });

Deno 1.x — with --unstable-cron flag

On Deno 1.38–1.x, pass --unstable-cron at startup. The API is identical; only the startup flag differs:

# deno run --unstable-cron --allow-net --allow-env job.ts // job.ts (same API as Deno 2.x) const PING = Deno.env.get("LASTPING_URL") ?? "https://ping.lastping.dev/<your-monitor-id>"; Deno.cron("nightly-etl", "0 2 * * *", async () => { try { await runJob(); await fetch(PING); } catch (err) { await fetch(`${PING}/fail`, { method: "POST", body: String(err).slice(0, 10_000), }); throw err; } });

Deno Deploy and external cron vs. Deno.cron

On Deno Deploy, Deno.cron runs inside the isolate and is the recommended approach. For scripts invoked externally by a system cron, use the same try/catch pattern but wrap the top-level await call rather than a Deno.cron handler. See also the Node.js guide for a comparison and the LastPing MCP server for AI-agent provisioning of monitors.


What this catches

The catch reports what it can; the silence reports the rest.

  • A thrown error — the /fail ping opens an incident immediately with the stack trace stored on the ping event.
  • An unhandled rejection — if the outer try/catch doesn't catch it, Deno marks the cron run as failed; the absence of a success ping opens the incident.
  • Process kill — no ping fires; the missing ping opens the incident after the grace window.
  • Deploy failure — new deploy that crashes on startup never registers the cron handler; absence → incident.
  • Recovery — the next successful run closes the incident and sends a recovery notice automatically.

Questions people ask

Front-loaded answers — the most important fact first.

  • Does Deno.cron require any flags in Deno 2?

    No. Deno.cron is stable in Deno 2.x and requires no flags. On Deno 1.38+ it was behind --unstable-cron. Check your version with deno --version.

  • Do I need npm or a third-party package?

    No. Deno ships fetch and Deno.cron built in. The entire integration is zero-dependency — no npm, no JSR package, no import map entry.

  • Does this work on Deno Deploy?

    Yes. Deno.cron is supported on Deno Deploy and runs inside the isolate. Each invocation gets its own execution context, so the try/catch wraps the full body of each tick.

  • Is this free?

    Yes. LastPing is free and fully hosted with no monitor cap and no paid tier. See how it compares to Healthchecks.io and Cronitor.

FREE · FULLY HOSTED · NO PAID TIER

Built-in fetch, native cron, zero setup — just add the ping.

Five lines now beat debugging a silent scheduled task later. First alert wired up in about a minute — and LastPing monitors itself the same way.