Uhtml is a micro-sized alternative to lighterhtml — the same template-literal-based UI rendering in a fraction of the bytes. It's perfect for lightweight web apps that need fast, reactive rendering without framework overhead. But small bundle size doesn't mean immunity from downtime. Vigilmon provides continuous HTTP monitoring, heartbeat checks, and alert delivery so you catch failures the moment they occur rather than when users start complaining.
What You'll Build
- A
/healthendpoint for your uhtml Node.js backend - Vigilmon HTTP monitors for your app and API
- A heartbeat monitor for background tasks
- Alert channels (email and Slack)
- An uptime badge in your uhtml UI
Prerequisites
- A Node.js project using uhtml (with Express or a plain HTTP server)
- A free Vigilmon account
Step 1: Add a Health Endpoint
Uhtml handles client-side rendering; your backend needs an HTTP health endpoint that Vigilmon can probe:
// server.js
import express from "express";
const app = express();
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 (e.g. Redis)
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,
});
});
app.use(express.static("public"));
app.listen(process.env.PORT ?? 3000);
Test locally:
curl http://localhost:3000/health | jq .
# {"status":"ok","timestamp":"2026-06-29T...","checks":{"database":"ok","cache":"ok"}}
Step 2: Configure Vigilmon HTTP Monitors
Log in to Vigilmon → Monitors → New Monitor:
Monitor 1: Uhtml App (Frontend)
| Field | Value |
|---|---|
| URL | https://yourapp.com/ |
| Method | GET |
| Expected status | 200 |
| Expected body | A stable string from your HTML shell |
| Check interval | 1 minute |
Monitor 2: Health API
| Field | Value |
|---|---|
| URL | https://yourapp.com/health |
| Method | GET |
| Expected status | 200 |
| Expected body | "status":"ok" |
| Check interval | 1 minute |
Two independent monitors mean a CDN misconfiguration (frontend down, API up) is distinct from a database failure (API down, frontend still served).
Step 3: Heartbeat for Background Tasks
Uhtml apps that fetch remote data often have background processes — polling APIs, refreshing tokens, syncing state. A Vigilmon heartbeat monitor detects when these jobs stop running silently:
// background.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 {
// Heartbeat errors must never crash the background job
}
}
// Run every 15 minutes
cron.schedule("*/15 * * * *", async () => {
try {
await syncRemoteData();
await pingHeartbeat();
} catch (err) {
console.error("[background] Data sync failed:", err);
// No ping → Vigilmon alerts after the grace window
}
});
async function syncRemoteData() {
// Fetch and cache data your uhtml UI consumes
}
Set up the heartbeat in Vigilmon:
- Monitors → New Heartbeat Monitor
- Set Expected interval to 30 minutes
- Add to your environment:
VIGILMON_HEARTBEAT_URL=https://vigilmon.online/api/heartbeat/xxxxxxxx
Step 4: Embed an Uptime Badge in Your Uhtml UI
Uhtml's tagged template literals make it easy to include a live status badge in your rendered output:
// app.js (client-side with uhtml)
import { html, render } from "uhtml";
const BADGE_URL = "https://vigilmon.online/api/badge/your-monitor-id.svg";
const STATUS_URL = "https://vigilmon.online/status/your-monitor-slug";
function Footer() {
return html`
<footer class="footer">
<a
href="${STATUS_URL}"
target="_blank"
rel="noopener noreferrer"
aria-label="Service uptime"
>
<img src="${BADGE_URL}" alt="Uptime" width="120" height="20" />
</a>
</footer>
`;
}
function App(data) {
return html`
<div>
<main>${renderMain(data)}</main>
${Footer()}
</div>
`;
}
render(document.getElementById("app"), () => App(appData));
Because uhtml only re-renders the parts of the DOM that change, the badge sits in the footer without interfering with your reactive state updates.
Step 5: Set Up Alert Channels
In Vigilmon's Alert Channels settings:
- Alert Channels → Email → add your on-call address
- Attach to all monitors
Slack
- Create a Slack Incoming Webhook for
#alerts - Alert Channels → Webhook → paste the URL
- 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 check
curl -s https://yourapp.com/health | jq .status
# 2. Simulate degraded state — remove DB credentials and redeploy
# Expected: 503 returned; Vigilmon alert fires within ~2 minutes
# 3. Remove the heartbeat URL and let the scheduler run one cycle
# Expected: heartbeat alert fires after the expected interval elapses
# 4. Vigilmon → "Test Alert" to verify email and Slack delivery
Summary
| Monitor | What It Catches | |---|---| | HTTP app check | Static file serving failures, CDN outages | | HTTP health API | Database, cache, and external dependency failures | | Heartbeat | Silent background job crashes |
Next Steps
- Create separate Vigilmon projects for staging and production
- Use Vigilmon's response time graphs to detect performance regressions when your data payloads grow
- Configure multi-channel alerting with different severity thresholds
Ready to get started? Sign up for Vigilmon free — have your first monitor running in under 5 minutes.