Skip to content
LastPing
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.

Your assistant can do this for you

Monitor my Deno scripts with LastPing

On this page
  1. The snippets
  2. What this catches
  3. Questions people ask

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.

  • How do I monitor a Deno cron job?

    Use Deno.cron to register your task. Inside the async handler, wrap your job in try/catch: on success await fetch('https://ping.lastping.dev/<your-monitor-id>'); on error await fetch('/fail', { method: 'POST', body: String(err).slice(0, 10000) }) and re-throw.

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

    No. Deno.cron is stable in Deno 2.x and available without flags. In Deno 1.x (1.38+) it was unstable and required --unstable-cron. Check your Deno version with deno --version.

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

    No. Deno ships fetch and the native Deno.cron API 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.

  • What if the Deno process is killed?

    A killed process sends no ping. Absence-detection monitoring handles this: the missing ping opens an incident after the grace window without any code running on the failure side.

  • Is Deno cron monitoring free?

    Yes. LastPing is free for individuals with no monitor cap. See how it compares to Healthchecks.io and Cronitor.

FREE FOR INDIVIDUALS · FULLY HOSTED

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.