How the Uptime Kuma Monitoring System Works Internally: Architecture and Code Deep Dive

Uptime Kuma operates as a Node.js producer-consumer pipeline that schedules health checks via the croner package, stores results in SQLite, broadcasts real-time updates through Socket.io, and dispatches alerts when monitor statuses change.

Uptime Kuma is an open-source Node.js application (available at louislam/uptime-kuma) that continuously monitors the health of websites, services, and network endpoints. The Uptime Kuma monitoring system combines a SQLite database for persistence, a job scheduler for check coordination, and a real-time event bus for instant status updates. Understanding its internal workflow requires examining the model layer in server/model/, the scheduler in server/jobs.js, and the notification pipeline in server/notification.js.

Monitor Definition and Job Scheduling

Every monitored resource is represented by a row in the monitor table and instantiated as a Monitor model in server/model/monitor.js. The model encapsulates the check interval (in seconds) or a custom cron expression that determines when the monitor executes.

The scheduler lives in server/jobs.js and utilizes the croner npm package to create jobs for every active monitor. When a monitor uses an interval, the system translates seconds into a cron pattern; otherwise, it uses the user-defined cron string.

// Creating a monitor (used by the API route in server/routers/api-router.js)
const monitor = await Monitor.add({
    name: "My Site",
    type: "http",
    interval: 60,               // every minute
    url: "https://example.com",
    httpMethod: "GET",
    authUser: "",
    authPass: "",
    body: "",
    headers: {}
});
// From server/jobs.js – registers a job for each monitor
const scheduleMonitor = (monitor) => {
    const pattern = monitor.interval
        ? `*/${monitor.interval} * * * * *`   // every N seconds
        : monitor.cron;                      // custom cron
    const job = new Cron(pattern, async () => {
        await monitor.run();                // dispatches to the monitor‑type
    });
    monitorJobMap.set(monitor.id, job);
};

Protocol-Specific Monitor Types

When a scheduled job fires, server/model/monitor.js dynamically loads the appropriate implementation from server/monitor-types/ based on the monitor's type property. Each type class exposes a ping method that receives a client instance (such as a got HTTP client or Globalping instance) and returns a heartbeat object containing status, msg, ping (ms), and TLS information.

// Core monitor execution (server/model/monitor.js)
async run() {
    const typeClass = require(`../monitor-types/${this.type}`);
    const monitorType = new typeClass(this);
    const hb = await monitorType.ping(); // returns {status, msg, ping, …}
    await hb.save();                     // persists to DB
    io.to(this.user_id).emit("heartbeat", hb.toJSON()); // real‑time push
}

Specific implementations include:

Heartbeat Persistence and Uptime Calculation

After a monitor type generates a heartbeat, the result is persisted to the heartbeat table via server/model/heartbeat.js. The uptime-calculator.js module subsequently aggregates these rows to compute uptime percentages, average response times, and downtime periods across arbitrary time windows.

This separation of concerns allows the check execution to remain lightweight while background processes handle statistical analysis. The heartbeat records maintain foreign key relationships to their parent monitors, enabling efficient historical queries through the REST API defined in server/routers/api-router.js.

Real-Time Communication and Notification Pipeline

Uptime Kuma employs Socket.io to push updates to connected dashboards immediately upon heartbeat insertion. The server/socket-handlers/monitor-socket-handler.js file manages the heartbeat event emission, ensuring the Vue-based UI reflects status changes without polling.

Simultaneously, the notification engine in server/notification.js evaluates whether the new heartbeat represents a status transition (e.g., from UP to DOWN). When a transition occurs, the system iterates through all enabled notification providers in server/notification-providers/ (such as telegram.js or slack.js) and dispatches formatted messages.

// Notification trigger (server/notification.js)
if (previous.status !== hb.status) {          // status change detected
    for (const n of await this.getEnabledNotifications(this.id)) {
        await NotificationProvider.send(
            n,
            `Monitor ${this.name} is now ${hb.status}`,
            this.toPublicJSON(),
            hb.toPublicJSON()
        );
    }
}

Maintenance Windows and External Integration

Maintenance Mode functionality resides in server/model/maintenance.js, which schedules suppression windows using separate croner jobs. While active, these windows inhibit alert generation for affected monitors, preventing false-positive notifications during planned downtime.

For external observability, server/prometheus.js implements a Prometheus exporter that aggregates recent heartbeat data and exposes it at the /metrics endpoint. This allows external monitoring systems to scrape Uptime Kuma's internal health metrics. The main server entry at server/uptime-kuma-server.js bootstraps Express, Socket.io, and the job scheduler to coordinate these components.

Summary

  • Uptime Kuma orchestrates health checks through a Node.js scheduler that instantiates protocol-specific classes from server/monitor-types/ to execute pings.
  • The croner package drives the scheduling engine in server/jobs.js, supporting both interval-based and cron-based execution patterns.
  • Check results are stored as heartbeats in SQLite via server/model/heartbeat.js, with uptime-calculator.js aggregating statistics for dashboards.
  • Socket.io broadcasts real-time updates through server/socket-handlers/monitor-socket-handler.js, while the notification engine in server/notification.js dispatches alerts only on status transitions.
  • Maintenance windows and Prometheus metrics extend the core pipeline with operational flexibility and external monitoring integration.

Frequently Asked Questions

How does Uptime Kuma schedule monitor checks?

Uptime Kuma uses the croner npm package in server/jobs.js to schedule checks. For interval-based monitors, it converts the seconds value into a cron pattern; for cron-based monitors, it uses the provided expression directly. Each active monitor receives its own Cron job instance that invokes monitor.run() from server/model/monitor.js.

What happens when a monitor status changes from UP to DOWN?

When server/model/monitor.js detects a status change during heartbeat generation, it emits a Socket.io event for real-time UI updates via server/socket-handlers/monitor-socket-handler.js. Simultaneously, server/notification.js compares the previous and current heartbeat statuses, then triggers all enabled notification providers with the monitor details and new status.

Where does Uptime Kuma store monitoring data?

All monitor definitions reside in the monitor table, while individual check results are stored in the heartbeat table, both managed through SQLite by server/model/monitor.js and server/model/heartbeat.js respectively. The uptime-calculator.js module queries these tables to compute uptime percentages and response time averages on demand.

How can I add a custom monitor type to Uptime Kuma?

Create a new JavaScript file in server/monitor-types/ that exports a class implementing a ping method returning a heartbeat object. The Monitor.run() method in server/model/monitor.js dynamically requires files from this directory based on the monitor's type property, making the system extensible without modifying core scheduling logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →