The problem: your Node.js app fails silently
You've deployed your Node.js app. CI is green. You go to sleep.
At 2am, your Express server crashes. A database connection pool exhausts. Your cron job that sends daily summaries starts silently failing. Nobody notices for three days.
Sound familiar? This is the silent failure problem that haunts production Node.js apps — and it's surprisingly easy to fix with two types of monitoring.
Two monitors every Node.js app needs
1. HTTP uptime monitoring
Your app exposes a /health endpoint. A monitoring service hits it every 60 seconds from multiple regions. If it goes down, you get alerted immediately.
2. Heartbeat monitoring (dead man's switch for cron jobs)
Your cron job tells the monitoring service it ran successfully. If the ping stops arriving, you get alerted.
This inverted approach catches silent failures that traditional monitoring completely misses.
Step 1: Add a health endpoint to Express
Start with a basic health check:
const express = require('express');
const app = express();
app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
app.listen(3000);
For production, check your database connection too:
const { Pool } = require('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
app.get('/health', async (req, res) => {
try {
await pool.query('SELECT 1');
res.json({ status: 'ok', db: 'connected', uptime: process.uptime() });
} catch (err) {
res.status(503).json({ status: 'error', db: 'disconnected' });
}
});
A 503 on your /health route means the monitoring service will alert you the moment the DB connection drops — not three hours later when users start complaining.
Step 2: Set up HTTP monitoring with Vigilmon
Vigilmon is a free, open-source uptime monitor that uses multi-region probes (Africa, UK, US-East). Unlike single-probe services, it only alerts when a majority of probes agree the site is down — eliminating 3am false positives from transient network blips.
Create your first monitor:
- Sign up at app.vigilmon.online — free tier, no credit card
- Click Add Monitor → HTTP
- Enter your URL:
https://your-app.example.com/health - Set check interval: 60 seconds
- Expected status code: 200
That's it. If your /health endpoint returns a non-200 status or times out, Vigilmon notifies you within 2–3 minutes.
Step 3: Set up heartbeat monitoring for your cron jobs
Heartbeat monitoring catches what HTTP monitoring can't: a cron job that stops running.
Create a Heartbeat monitor in Vigilmon:
- Click Add Monitor → Heartbeat
- Set the expected interval: 24 hours (or whatever your job's cadence is)
- Copy the generated ping URL:
https://app.vigilmon.online/api/heartbeats/ping/YOUR_TOKEN
Add the ping to your Node.js cron job:
Using node-cron:
const cron = require('node-cron');
cron.schedule('0 0 * * *', async () => {
try {
// Your actual job logic
await runDailyBackup();
await syncUserData();
// Only ping if the job completed successfully
await fetch('https://app.vigilmon.online/api/heartbeats/ping/YOUR_TOKEN');
console.log('Daily job completed');
} catch (err) {
console.error('Daily job failed:', err);
// No ping = Vigilmon alerts you when the interval expires
}
});
Using node-schedule:
const schedule = require('node-schedule');
const job = schedule.scheduleJob('0 0 * * *', async function() {
try {
await processNightlyReport();
await fetch('https://app.vigilmon.online/api/heartbeats/ping/YOUR_TOKEN');
} catch (err) {
console.error('Nightly report failed:', err);
// Vigilmon detects the missing ping and alerts you
}
});
The key insight: only ping on success. If your job throws, the ping never fires. When Vigilmon doesn't receive a ping within the expected window, you get alerted — even if the job is failing silently with no errors logged anywhere.
Step 4: Configure Slack and email alerts
In Vigilmon's notification settings, you can wire up:
Email: Alerts go to your registered email when any monitor goes down or a heartbeat goes missing.
Slack webhook:
- Create an Incoming Webhook in your Slack workspace (Apps → Incoming Webhooks)
- In Vigilmon, go to Settings → Notifications → Slack
- Paste the webhook URL
- Choose which monitors trigger Slack alerts
Your #ops channel will receive a message the moment something breaks — no manual checking required.
Custom webhooks: Vigilmon can POST to any URL with the monitor status, timestamps, and probe details. Use this to integrate with PagerDuty, OpsGenie, or any alerting pipeline.
What this setup catches
| Failure scenario | Detection method |
|---|---|
| Express server crashes or OOM killed | HTTP monitor on /health
|
| Database connection pool exhausted |
/health returns 503 |
| Cron job throws an unhandled error | Heartbeat goes missing |
| Cron job stops running (scheduler bug) | Heartbeat goes missing |
| Slow responses / degraded performance | Response time history + P50/P95 charts |
| SSL certificate about to expire | SSL expiry monitor (warns 30 days out) |
Self-hosting option
Vigilmon is fully open-source (MIT). If you need to keep monitoring on-premise or want to customize it:
git clone https://github.com/isak-ialogics/vigil
cd vigil
cp .env.example .env
docker compose up -d
Stack: Laravel 12 / PHP 8.4, PostgreSQL + TimescaleDB for time-series data, FrankenPHP.
Summary
Two monitors. One for your HTTP endpoint, one for each scheduled job. That's the minimum to have real production visibility for a Node.js app.
Free tier on Vigilmon includes:
- Up to 5 monitors
- 60-second check intervals
- Multi-region probes (no single point of failure)
- Email and Slack alerts
- Response time history with P50/P95 charts
Get started: app.vigilmon.online
Self-host: github.com/isak-ialogics/vigil
Top comments (1)
If you're running your Node.js app in Docker, check out our follow-up article: How to Monitor Docker Containers with Vigilmon (HTTP, TCP, and Alerts) — covers health check endpoints, TCP port monitoring, webhook alerts, and a public status page for containerized services.