Uptime Kuma Status Page Architecture: Full-Stack Server-Rendered Design with Real-Time Updates

Uptime Kuma implements its status page system as a four-layer, server-rendered architecture that embeds JSON data directly into HTML for instant Vue hydration while using Socket.IO to synchronize administrative changes in real time.

The Uptime Kuma status page architecture combines server-side rendering (SSR) for public visibility with dynamic WebSocket updates for management interfaces. This design resides in the louislam/uptime-kuma repository and leverages Express.js for routing, a centralized StatusPage model for business logic, and Vue.js for presentation, enabling both SEO-friendly public pages and real-time administrative dashboards.

Four-Layer System Architecture

The status page implementation spans four distinct layers that handle HTTP requests, business logic, real-time communication, and frontend rendering.

HTTP Routing Layer

The entry point resides in server/server.js, which registers the status page router. The server/routers/status-page-router.js file maps /status/:slug and /api/status-page/:slug/* endpoints, including the RSS feed route. This layer validates the slug against the database and initiates the server-side rendering process.

Model Layer (Business Logic)

Located in server/model/status_page.js, this layer contains the StatusPage class responsible for loading bean data, assembling public configurations, aggregating incidents and maintenance windows, and grouping monitors. It provides handleStatusPageResponse for SSR, renderRSS for feed generation, and manages domain mapping through loadDomainMappingList().

Socket Layer

The server/socket-handlers/status-page-socket-handler.js manages real-time updates for administrative clients. It emits events like statusPageList and statusPageUpdate to specific Socket.IO rooms identified by socket.userID, ensuring immediate synchronization when administrators create, modify, or delete status pages.

Frontend Layer

Vue components in src/pages/StatusPage.vue (public view) and src/pages/ManageStatusPage.vue (admin interface) consume the preloaded data. The src/mixins/theme.js mixin handles automatic light/dark theme switching based on the page configuration, while src/mixins/socket.js listens for server-side events to refresh the admin UI dynamically.

HTTP Request Flow and Server-Side Rendering

When a visitor requests a public status page, the system executes a precise sequence to deliver optimized HTML with embedded data.

First, the Express router in server/routers/status-page-router.js receives the request and queries the database using RedBean ORM:

// server/routers/status-page-router.js
router.get("/:slug", async (req, res) => {
    let statusPage = await R.findOne("status_page", " slug = ? ", [slug]);
    if (!statusPage) return res.status(404).send(...);
    await StatusPage.handleStatusPageResponse(res, indexHTML, slug);
});

The StatusPage.handleStatusPageResponse method in server/model/status_page.js orchestrates the response generation. It invokes renderHTML, which injects three critical elements into the base index.html template: SEO meta tags (title, description, Open Graph), an optional analytics script, and a <script id="preload-data"> tag containing the JSON payload.

The JSON data is escaped using the jsesc library to prevent XSS attacks and is exposed globally as window.preloadData. This allows the Vue application in src/pages/StatusPage.vue to hydrate immediately without additional API requests, eliminating loading spinners for public visitors.

Real-Time Updates via Socket.IO

Administrative operations rely on bidirectional communication through Socket.IO. The server/socket-handlers/status-page-socket-handler.js registers event listeners for CRUD operations:

// server/socket-handlers/status-page-socket-handler.js
socket.on("addStatusPage", async (config, callback) => {
    // Validation and database operations
    // Emits statusPageList to refresh admin views
});

When an administrator modifies a status page, the server emits targeted events to the user's specific room (socket.userID). The frontend mixin in src/mixins/socket.js captures these events and updates the Vue state:

// src/mixins/socket.js
socket.on('statusPageList', data => {
    this.statusPageListLoaded = true;
    this.statusPageList = data;
});

This architecture ensures that multiple administrators viewing the management interface receive instantaneous updates without manual page refreshes.

RSS Feed Architecture

The status page system generates RSS 2.0 feeds for monitoring down events. The router maps GET /api/status-page/:slug/rss to StatusPage.handleStatusPageRSSResponse in the model layer.

The renderRSS method gathers down heartbeats via getRSSPageData, constructs a Feed object using the feed npm package, and returns XML. The buildRSSUrl helper function respects reverse proxy headers (X-Forwarded-Proto, X-Forwarded-Host) when trustProxy is enabled, ensuring correct feed URLs behind load balancers.

// server/model/status_page.js
static async renderRSS(statusPage, feedUrl) {
    // Constructs RSS XML from down heartbeats
}

Custom Domain Mapping (CNAME Support)

Uptime Kuma supports custom domains through the status_page_cname database table. The StatusPage.loadDomainMappingList() method populates a static domainMappingList map at runtime.

The main application entry point in server/server.js inspects the request.headers.host header against this mapping. When a match occurs, the server redirects the custom domain to the appropriate /status/:slug endpoint, allowing organizations to host status pages on branded domains like status.company.com.

Frontend Data Consumption and Theming

The public status page in src/pages/StatusPage.vue reads the server-injected window.preloadData during initialization. This data includes monitor groups, incident history, maintenance schedules, and theme configuration.

The src/mixins/theme.js mixin automatically switches between light and dark modes when the page configuration specifies theme: "auto". This occurs client-side after the initial SSR render, respecting user preferences without requiring server-side theme detection.

Summary

  • Four-layer architecture: HTTP routing, Model logic, Socket.IO real-time layer, and Vue frontend operate independently but synchronize through defined interfaces.
  • Server-side rendering: The StatusPage.renderHTML method in server/model/status_page.js embeds JSON data directly into the HTML using jsesc for immediate Vue hydration and SEO optimization.
  • Real-time administration: Socket.IO events in status-page-socket-handler.js push updates to src/mixins/socket.js, keeping management interfaces synchronized without page reloads.
  • Extensibility: The architecture supports RSS feeds via the feed package and custom CNAME domains through the status_page_cname table and host header inspection in server/server.js.

Frequently Asked Questions

How does Uptime Kuma handle SEO for status pages?

Uptime Kuma generates server-side rendered HTML in server/model/status_page.js through the renderHTML method, which injects meta tags for titles, descriptions, and Open Graph properties directly into the page source. This ensures search engines index the content properly while the Vue application hydrates from the embedded window.preloadData JSON blob.

What enables real-time updates in the status page admin panel?

The system uses Socket.IO handlers defined in server/socket-handlers/status-page-socket-handler.js to emit events like statusPageList and statusPageUpdate to rooms identified by socket.userID. The frontend mixin in src/mixins/socket.js listens for these events and updates the Vue component state immediately, reflecting changes made by any administrator across all active sessions.

Can status pages be hosted on custom domains?

Yes. The architecture supports custom CNAME mapping through the status_page_cname database table. The StatusPage.loadDomainMappingList() method maintains a domain-to-slug mapping, and server/server.js checks the Host header against this list to route requests from custom domains to the correct /status/:slug endpoint.

How is the initial page data loaded so quickly?

Public pages utilize server-side data injection where StatusPage.handleStatusPageResponse embeds a JSON payload escaped with jsesc into a <script id="preload-data"> tag within the initial HTML response. The Vue frontend in src/pages/StatusPage.vue reads window.preloadData during initialization, eliminating the need for separate API calls and enabling instantaneous rendering.

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 →