tutorial

Monitoring Deno Deploy with Vigilmon

Add external uptime monitoring to Deno Deploy edge functions — health check handlers, Vigilmon HTTP monitors, and alerting on regional edge failures and deployment issues.

Monitoring Deno Deploy with Vigilmon

Deno Deploy runs your TypeScript and JavaScript at the edge in 35+ regions worldwide. Deployments are instant, there's no Docker to manage, and the global distribution means low latency for users everywhere.

But global distribution also means global failure modes. An edge function that errors in Tokyo may still respond correctly in Frankfurt. A deployment that breaks an import path silently affects all regions simultaneously. Standard application logs don't tell you whether users in a specific region are hitting errors right now.

Vigilmon gives you an external health check that probes your Deno Deploy endpoint from multiple locations and alerts you the moment something breaks — regardless of which region fails first.

Note: This tutorial covers Deno Deploy — the hosted edge deployment platform by Deno. For monitoring a self-hosted Deno application on your own server, the patterns are different. This guide is specifically for the Deno Deploy hosting platform.


What You'll Build

  • A /health handler inside your Deno Deploy project
  • A Vigilmon HTTP monitor targeting your .deno.dev endpoint
  • Alert channels for deployment failure and regional degradation
  • A heartbeat monitor for Deno Deploy Cron

Prerequisites

  • A Deno Deploy project (linked to a GitHub repo or via deployctl)
  • A free account at vigilmon.online

Step 1: Add a health handler to your Deno Deploy project

Deno Deploy uses a single entry file with a Deno.serve() handler. Add a /health route that checks your real dependencies.

main.ts (Deno Deploy entry file):

import { serve } from "https://deno.land/std@0.224.0/http/server.ts";

// Replace with your actual KV or external dependency
const kv = await Deno.openKv();

serve(async (req: Request): Promise<Response> => {
  const url = new URL(req.url);

  if (url.pathname === "/health") {
    return await handleHealth(req);
  }

  // ... rest of your routing
  return new Response("Not found", { status: 404 });
});

async function handleHealth(_req: Request): Promise<Response> {
  const checks: Record<string, string> = {};
  let ok = true;

  // Deno KV probe — write and read a sentinel key
  try {
    const key = ["_health", "probe"];
    await kv.set(key, "1", { expireIn: 60_000 });
    const result = await kv.get<string>(key);
    checks.kv = result.value === "1" ? "ok" : "read_mismatch";
    if (checks.kv !== "ok") ok = false;
  } catch (err) {
    checks.kv = `error: ${(err as Error).message}`;
    ok = false;
  }

  // External API probe (replace with your real dependency)
  try {
    const resp = await fetch("https://api.example.com/ping", {
      signal: AbortSignal.timeout(3_000),
    });
    checks.upstream = resp.ok ? "ok" : `http_${resp.status}`;
    if (!resp.ok) ok = false;
  } catch (err) {
    checks.upstream = `error: ${(err as Error).message}`;
    ok = false;
  }

  return Response.json(
    {
      status: ok ? "ok" : "degraded",
      region: Deno.env.get("DENO_REGION") ?? "unknown",
      checks,
    },
    { status: ok ? 200 : 503 }
  );
}

Deno Deploy injects DENO_REGION automatically — include it in your health response so Vigilmon's check logs tell you exactly which edge region degraded.

Deploy your change:

# Via deployctl
deployctl deploy --project=my-project main.ts

# Or push to your linked GitHub repo — Deno Deploy auto-deploys on every push
git push origin main

Your health endpoint is available at:

https://my-project.deno.dev/health

Step 2: Add Vigilmon HTTP Monitor

  1. Log in to Vigilmon and click New Monitor → HTTP
  2. URL: https://my-project.deno.dev/health
  3. Check interval: 1 minute (paid) or 5 minutes (free)
  4. Response timeout: 10 seconds
  5. Expected status: 200
  6. Under Advanced, enable JSON body assertion:
    • Path: status
    • Expected value: ok
  7. Save

Vigilmon now probes your Deno Deploy endpoint from multiple external locations and alerts you the moment it returns a non-200 response or "status": "degraded".

Alert channels

Go to Notifications → New Channel and configure:

  • Email — your on-call address
  • Webhook — Slack incoming webhook or PagerDuty endpoint for team visibility

Step 3: Catching deployment failures

Deno Deploy deployments are atomic — a new deployment either succeeds or fails, and traffic switches instantly. If a deployment breaks an import or introduces a runtime error, all your users are affected simultaneously.

Vigilmon will catch this within the first check interval after deployment.

To minimize risk, use Deno Deploy Preview deployments for testing before production:

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to Deno Deploy
        uses: denoland/deployctl@v1
        with:
          project: my-project
          entrypoint: main.ts

When a deployment causes Vigilmon to alert, roll back by redeploying the previous commit:

# Find the previous successful deployment hash
git log --oneline -5

# Redeploy the previous version
git revert HEAD --no-commit
git commit -m "revert: roll back broken deployment"
git push origin main

Step 4: Cron job heartbeat monitoring

Deno Deploy supports Cron for scheduled tasks. If your cron handler fails silently, standard HTTP monitoring won't catch it.

Add a Vigilmon heartbeat ping at the end of each successful cron run:

// main.ts — cron handler
Deno.cron("data-sync", "*/15 * * * *", async () => {
  try {
    await runDataSync();

    // Ping Vigilmon heartbeat only on success
    const heartbeatUrl = Deno.env.get("VIGILMON_CRON_HEARTBEAT");
    if (heartbeatUrl) {
      await fetch(heartbeatUrl, { method: "GET" });
    }

    console.log("Cron: data-sync complete");
  } catch (err) {
    console.error("Cron: data-sync failed", err);
    // Do NOT ping heartbeat — Vigilmon will alert on missing ping
  }
});

async function runDataSync() {
  // your scheduled work
}

Store the heartbeat URL in Deno Deploy's environment variable settings:

  1. Go to Deno Deploy dashboard → your project → Settings → Environment Variables
  2. Add VIGILMON_CRON_HEARTBEAT = https://vigilmon.online/heartbeat/your-monitor-id

In Vigilmon, create a Heartbeat Monitor:

  1. Click New Monitor → Heartbeat
  2. Set the expected interval to match your cron schedule (e.g., 15 minutes)
  3. Set the grace period to 20 minutes
  4. Copy the ping URL into your Deno Deploy env var

If Vigilmon doesn't receive a ping within the grace period, it fires an alert.


Step 5: Custom domain monitoring

If you use a custom domain with Deno Deploy, add a second Vigilmon monitor for the custom domain alongside the .deno.dev monitor:

  1. Add a monitor for https://api.yourdomain.com/health
  2. This catches DNS misconfiguration and TLS certificate issues that a .deno.dev monitor won't detect

Group both monitors under a single Status Page — visitors see one aggregate status without knowing your internal infrastructure.


What Vigilmon catches that Deno's dashboard misses

| Scenario | Deno Deploy Dashboard | Vigilmon | |---|---|---| | New deployment breaks an import | Shows error in logs after the fact | HTTP monitor alerts within 1 minute | | Edge function errors in one region | Aggregated across all regions | Per-check log shows failing region | | Deno KV write failure | Requires log analysis | /health KV probe alerts immediately | | Cron job stops running | No built-in alerting | Heartbeat grace period alerts | | Custom domain DNS issue | Not monitored | Custom domain monitor catches it |


Deno Deploy is fast and globally distributed, but it still fails. External monitoring from Vigilmon gives you the independent vantage point you need to catch failures before your users file bug reports.

Set up monitoring for your Deno Deploy project 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 →