tutorial

Monitoring Your Marko.js App with Vigilmon: Health Checks, Heartbeats & Uptime Alerts

Add production-grade monitoring to a Marko.js application — a health API, background job heartbeats, and instant alerts when your streaming SSR server or backend goes down.

Marko.js is eBay's battle-tested HTML-first framework, renowned for streaming server-side rendering and exceptional time-to-first-byte. But fast SSR only helps when your server is actually running. Vigilmon gives you the visibility layer you need: continuous HTTP monitoring, heartbeat checks, and instant alerts routed to email or Slack.

What You'll Build

  • A health API endpoint for your Marko.js server
  • Vigilmon HTTP monitors for your Marko app and backend services
  • A heartbeat for scheduled background tasks
  • Alert channels (email and Slack)
  • An uptime badge on your Marko pages

Prerequisites

  • A Marko.js project (@marko/run or a custom Express/Hapi setup)
  • Node.js for the health endpoint
  • A free Vigilmon account

Step 1: Add a Health Endpoint

Marko renders HTML server-side, so your health check lives in your Node.js server layer. Here's how to add one with Express:

// server.js
import express from "express";

const app = express();

// Health endpoint — register BEFORE Marko catch-all
app.get("/health", async (req, res) => {
  const checks = {};
  let degraded = false;

  // Database check
  try {
    // await db.query("SELECT 1");
    checks.database = "ok";
  } catch (err) {
    checks.database = `error: ${err.message}`;
    degraded = true;
  }

  // Cache check
  try {
    // await redisClient.ping();
    checks.cache = "ok";
  } catch (err) {
    checks.cache = `error: ${err.message}`;
    degraded = true;
  }

  res.status(degraded ? 503 : 200).json({
    status: degraded ? "degraded" : "ok",
    timestamp: new Date().toISOString(),
    checks,
  });
});

// Marko page routes
app.get("/", (req, res) => {
  res.setHeader("Content-Type", "text/html");
  require("./src/pages/index.marko").render({}, res);
});

app.listen(process.env.PORT ?? 3000);

Test it:

curl http://localhost:3000/health | jq .
# {"status":"ok","timestamp":"2026-07-03T...","checks":{"database":"ok","cache":"ok"}}

Step 2: Configure Vigilmon HTTP Monitors

Log in to VigilmonMonitors → New Monitor:

Monitor 1: Marko.js Application

| Field | Value | |---|---| | URL | https://yourapp.com/ | | Method | GET | | Expected status | 200 | | Expected body | A stable string from your rendered HTML (e.g. site title) | | Check interval | 1 minute |

Monitor 2: Health API

| Field | Value | |---|---| | URL | https://yourapp.com/health | | Method | GET | | Expected status | 200 | | Check interval | 1 minute |

Two monitors cover two independent failure classes: the Node process itself and the underlying data/service layer.


Step 3: Heartbeat for Background Tasks

Marko apps often handle heavy server-side data aggregation and scheduled rendering tasks. Use a Vigilmon heartbeat to catch silent failures:

npm install node-cron
// scheduler.js
import cron from "node-cron";

const VIGILMON_HEARTBEAT_URL = process.env.VIGILMON_HEARTBEAT_URL ?? "";

async function pingHeartbeat() {
  if (!VIGILMON_HEARTBEAT_URL) return;
  try {
    await fetch(VIGILMON_HEARTBEAT_URL, { signal: AbortSignal.timeout(5000) });
  } catch {
    // Monitoring must never crash the job
  }
}

// Cache-warming job — runs every 10 minutes
cron.schedule("*/10 * * * *", async () => {
  try {
    await warmPageCache();
    await pingHeartbeat();
  } catch (err) {
    console.error("[scheduler] Cache warming failed:", err);
  }
});

async function warmPageCache() {
  // Pre-render high-traffic Marko pages into your cache layer
}

Set up the heartbeat in Vigilmon:

  1. Monitors → New Heartbeat Monitor
  2. Set Expected interval to 20 minutes (2× the cron interval)
  3. Copy the ping URL to your .env:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx

Step 4: Display Uptime Status on a Marko Page

Add the badge as a Marko component:

<!-- src/components/status-badge.marko -->
<a
  href="https://vigilmon.online/status/your-monitor-slug"
  target="_blank"
  rel="noopener noreferrer"
  aria-label="Service uptime status"
>
  <img
    src="https://vigilmon.online/api/badge/your-monitor-id.svg"
    alt="Uptime"
    width=120
    height=20
    loading="lazy"
  />
</a>

Include it in your layout:

<!-- src/layouts/default.marko -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My Marko App</title>
  </head>
  <body>
    <${input.renderBody}/>
    <footer>
      <status-badge/>
    </footer>
  </body>
</html>

Step 5: Set Up Alert Channels

In Vigilmon's Alert Channels settings:

Email

  1. Alert Channels → Email → add your on-call email
  2. Attach to both HTTP monitors and the heartbeat

Slack

  1. Create a Slack Incoming Webhook for #alerts
  2. Alert Channels → Webhook → paste the URL
  3. Payload template:
{
  "text": "🚨 *{{ monitor.name }}* is DOWN\n{{ monitor.url }}\nStatus: {{ event.status }}\n<https://vigilmon.online|Open Vigilmon>"
}

Step 6: Verify Everything Works

# 1. Health endpoint
curl -s https://yourapp.com/health | jq .

# 2. Kill the database connection -> confirm health returns 503
# Expected: Vigilmon alerts within 2 minutes

# 3. Stop the scheduler -> wait for the heartbeat window
# Expected: heartbeat alert fires after the grace window

# 4. Vigilmon -> "Test Alert" -> confirm delivery to Slack/email

Summary

| Monitor | What It Catches | |---|---| | HTTP app check | Node process crash, SSR failures, broken deploys | | HTTP health API | Database, cache, and upstream service failures | | Heartbeat | Scheduler crashes, cache-warming failures |


Next Steps

  • Monitor your streaming response time — Vigilmon's response time graphs are ideal for catching Marko TTFB regressions
  • Create separate monitors for staging vs. production
  • Configure multi-channel alerting: email for low-severity, Slack for critical

Ready to get started? Sign up for Vigilmon free — have your first monitor running in under 5 minutes.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →