Uptime Kuma Uptime Calculator: How It Computes Uptime Percentages from Heartbeat Data

The Uptime Kuma uptime calculator aggregates raw monitor heartbeats into rolling minutely, hourly, and daily buckets, then computes uptime percentages by dividing total UP time by the combined UP and DOWN time.

The UptimeCalculator class in the louislam/uptime-kuma repository transforms raw heartbeat results into precise availability statistics. Located in server/uptime-calculator.js, this core component processes UP, DOWN, and MAINTENANCE statuses to generate the uptime percentages displayed throughout the application's badges and charts.

How the Uptime Kuma Uptime Calculator Aggregates Data

Processing Monitor Status Updates

The update(status, ping, date) method in server/uptime-calculator.js (starting at line 12) receives incoming heartbeat results and maps them to a flat status value. The flatStatus() helper (lines 42-51) consolidates MAINTENANCE and PENDING states into UP for calculation purposes, while the original status remains preserved for accurate UI display.

Rolling Window Buckets

The calculator maintains three in-memory queues using the LimitQueue utility from src/util.js:

  • Minutely: 24 hours of one-minute buckets (minutelyUptimeDataList)
  • Hourly: 30 days of one-hour buckets (hourlyUptimeDataList)
  • Daily: 365 days of one-day buckets (dailyUptimeDataList)

Persisting Statistics to the Database

After updating in-memory counters, the calculator writes aggregates to three database tables: stat_minutely, stat_hourly, and stat_daily (see the R.store() calls around lines 302-355). A retention policy prunes entries older than their window limits (lines 562-670), preventing unbounded database growth while preserving historical accuracy.

Computing Uptime Percentages

Retrieving Time-Window Data

Clients request uptime summaries through getData(num, type) or convenience wrappers including get24Hour(), get7Day(), get30Day(), and get1Year(). The method constructs a UTC-based time window using getCurrentDate() (line 331) and iterates backwards over the relevant buckets to sum counters.

The Uptime Calculation Formula

During aggregation (lines 715-718), the calculator sums the up and down counters while accumulating avgPing * up for average latency calculations. The final uptime percentage is computed at line 781 as:

uptime = total.up / (total.up + total.down);

If no data exists for the requested window, the calculator implements a lazy fallback to the latest known bucket (lines 738-768). The method returns an UptimeDataResult object containing the uptime ratio (0 to 1) and the average ping (or null when no UP data exists).

Key Design Decisions

Three Rolling Windows: Using separate minute, hour, and day granularity allows fast badge calculations for common timeframes without scanning the entire heartbeat table.

UTC-Based Timestamps: The calculator uses date.utc() (line 999) to generate consistent keys across time zones, eliminating daylight-saving anomalies.

Flat Status Conversion: Treating maintenance and pending states as UP ensures uptime calculations reflect actual service availability rather than administrative states.

Lazy Fallback Mechanism: When requested windows lack data (such as for newly created monitors), the calculator returns the last available bucket to prevent empty results.

Practical Implementation Examples

Recording a New Heartbeat

const { UptimeCalculator } = require("./server/uptime-calculator");

(async () => {
  const calc = await UptimeCalculator.getUptimeCalculator(monitorID);
  // Status: UP (0), DOWN (1), MAINTENANCE (2), PENDING (3)
  await calc.update(0, 34);  // Record UP with 34ms ping
})();

Retrieving 30-Day Uptime Statistics

const { UptimeCalculator } = require("./server/uptime-calculator");

(async () => {
  const calc = await UptimeCalculator.getUptimeCalculator(monitorID);
  const result = calc.get30Day();
  console.log(`Uptime: ${(result.uptime * 100).toFixed(2)}%`);
  console.log(`Avg Ping: ${result.avgPing?.toFixed(1)}ms`);
})();

Querying Custom Durations

const result = calc.getDataByDuration("2w");
// Supports: m (minutes), h (hours), d (days), w (weeks), 
//           M (months ≈ 30d), y (years ≈ 365d)

Core Source Files

Summary

  • The UptimeCalculator class in server/uptime-calculator.js aggregates heartbeats into minutely, hourly, and daily buckets to optimize query performance.
  • Flat status conversion treats MAINTENANCE and PENDING as UP for availability math while preserving original states for display.
  • Uptime percentages derive from total.up / (total.up + total.down) using pre-aggregated database tables rather than raw heartbeat scans.
  • Convenience methods like get24Hour() and get30Day() provide quick access to common reporting windows.
  • UTC-based timestamp keys ensure consistent calculations across global time zones and daylight-saving transitions.

Frequently Asked Questions

How does Uptime Kuma handle maintenance windows in uptime calculations?

The flatStatus() method maps MAINTENANCE and PENDING statuses to UP values for mathematical calculations, ensuring administrative actions do not artificially penalize availability metrics. The original status remains stored separately in the database for accurate UI display and reporting.

What database tables store the uptime calculator data?

The calculator persists aggregates to three tables: stat_minutely (24-hour retention), stat_hourly (30-day retention), and stat_daily (365-day retention). These tables store up and down counters plus ping statistics for each time bucket, enabling fast historical queries without scanning the full heartbeat log.

How does the calculator handle monitors with no historical data?

When a requested time window contains no records, the calculator implements a lazy fallback mechanism (lines 738-768) that returns data from the most recent available bucket. This ensures newly added monitors display reasonable defaults rather than empty or null results in the dashboard.

What is the retention policy for uptime statistics?

Old rows are automatically pruned during the update cycle (lines 562-670) when they exceed their respective window limits: 24 hours for minutely data, 30 days for hourly data, and 365 days for daily data. This prevents unbounded database growth while maintaining sufficient granularity for standard uptime reporting periods.

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 →