This guide covers the full operational surface of Uptime Kuma 2.2.1 for administrators, SREs, security engineers, and developers who deploy, scale, secure, and extend the platform. Every fact is derived from source code at commit 43b143c (2026-04-17).
Note
Uptime Kuma 2.x introduces a knex-based database abstraction layer supporting both SQLite and MariaDB, a plugin-based monitor-type system, and a restructured migration pipeline. This guide documents 2.x behavior exclusively unless a section explicitly references 1.x.
1. Architecture Overview
1.1 High-Level Topology
Uptime Kuma runs as a single Node.js process serving both an Express HTTP(S) server and a Socket.IO WebSocket gateway on the same port (default 3001). The Vue 3 SPA frontend is compiled by Vite and served as static assets by Express.
Client Browser
│
▼ HTTP + Socket.IO (WSS)
┌──────────────────────────────────────────────────────────────────┐
│ Express App (server/server.js) │
│ ├── Static file serving (Vue 3 SPA, compiled by Vite) │
│ ├── API Router (server/routers/api-router.js) │
│ │ ├── /api/push/:pushToken Push monitor receiver │
│ │ ├── /api/entry-page Landing page config │
│ │ └── /api/badge/:id/* SVG badge endpoints │
│ ├── Status Page Router (server/routers/status-page-router.js) │
│ │ ├── /status/:slug Public status page │
│ │ ├── /status/:slug/rss RSS feed │
│ │ └── /api/status-page/:slug/* Status page API │
│ ├── /metrics Prometheus scrape target │
│ └── Socket.IO Server │
│ └── 10 Socket Handlers │
│ ├── general-socket-handler Auth, monitors, settings │
│ ├── maintenance-socket-handler Maintenance windows │
│ ├── status-page-socket-handler Status page CRUD │
│ ├── cloudflared-socket-handler Tunnel management │
│ ├── docker-socket-handler Docker host management │
│ ├── proxy-socket-handler Proxy CRUD │
│ ├── database-socket-handler DB info, shrink, migrate │
│ ├── remote-browser-socket-handler Playwright browsers │
│ ├── api-key-socket-handler API key management │
│ └── chart-socket-handler Chart data retrieval │
├──────────────────────────────────────────────────────────────────┤
│ Monitor Engine │
│ ├── Monitor model (server/model/monitor.js) — heartbeat loop │
│ ├── 23 MonitorType plugins (server/monitor-types/*.js) │
│ ├── Monitor Conditions evaluator (server/monitor-conditions/) │
│ └── UptimeCalculator (server/uptime-calculator.js) — stats │
├──────────────────────────────────────────────────────────────────┤
│ Notification System │
│ ├── Dispatcher (server/notification.js) │
│ └── 93 NotificationProvider subclasses │
├──────────────────────────────────────────────────────────────────┤
│ Database Layer │
│ ├── Database (server/database.js) — SQLite or MariaDB via knex │
│ ├── 48 knex migrations (db/knex_migrations/) │
│ └── 13 ORM models (server/model/) via redbean-node │
├──────────────────────────────────────────────────────────────────┤
│ Background Jobs │
│ ├── clear-old-data.js — prune heartbeat history │
│ └── incremental-vacuum.js — SQLite PRAGMA incremental_vacuum │
├──────────────────────────────────────────────────────────────────┤
│ Singleton Lifecycle: UptimeKumaServer (server/uptime-kuma-server)│
│ Graceful Shutdown: http-graceful-shutdown (30s timeout) │
│ Signals: SIGINT, SIGTERM │
└──────────────────────────────────────────────────────────────────┘
│
▼
SQLite (default) or MariaDB
DATA_DIR/kuma.db or remote MariaDB instance
Note
Socket.IO is the primary API surface, not REST. The REST endpoints are limited to badge generation, push monitor ingestion, status pages, metrics, and setup info. All monitor CRUD, settings management, authentication, and real-time data flow happen over Socket.IO events.
1.2 Request Flow
- HTTP requests hit Express middleware (rate limiting, CORS, security headers, static file serving).
- Badge and push endpoints are handled by
api-router.jswithout authentication (public). - Status page requests are handled by
status-page-router.js(public, with 5-minute cache on badges). /metricsrequires HTTP Basic Auth against the Uptime Kuma user credentials.- All other operations flow through Socket.IO: the client connects, authenticates via
loginorloginByToken, and then emits events for every action.
1.3 Singleton Pattern
UptimeKumaServer (server/uptime-kuma-server.js) is the application singleton. It owns:
- The Express app and HTTP(S) server instances
- The Socket.IO server
- The monitor map (all active monitors keyed by ID)
- Cloudflared tunnel process
- Background job scheduler
- Graceful shutdown orchestration
All socket handlers and models access this singleton to coordinate state.
1.4 Database Architecture
The database layer (server/database.js) abstracts SQLite and MariaDB behind knex. The ORM layer uses redbean-node for model access.
Tables (23 total, defined in db/knex_init_db.js):
| Table | Purpose |
|---|---|
user |
Admin accounts (single-user or multi-user) |
monitor |
Monitor definitions and configuration |
heartbeat |
Individual check results with status, ping, message |
incident |
Status page incidents |
maintenance |
Maintenance windows with scheduling |
status_page |
Public status page definitions |
group |
Monitor groups for status pages |
proxy |
HTTP/SOCKS proxy definitions |
docker_host |
Docker daemon connections |
api_key |
API keys with expiry dates |
tag |
Tags for monitor organization |
monitor_tag |
Many-to-many: monitors ↔ tags |
monitor_notification |
Many-to-many: monitors ↔ notification channels |
notification |
Notification channel definitions (provider + config JSON) |
setting |
Key-value settings store |
notification_sent_history |
Deduplication for notification delivery |
domain_expiry |
Cached domain/TLS expiry data |
remote_browser |
Remote Playwright browser connections |
maintenance_timeslot |
Computed maintenance time ranges |
monitor_maintenance |
Many-to-many: monitors ↔ maintenance windows |
stat_minutely |
Aggregated per-minute statistics |
stat_hourly |
Aggregated per-hour statistics |
stat_daily |
Aggregated per-day statistics |
Migration system: 48 migration files in db/knex_migrations/ run sequentially on startup. The knex_migrations and knex_migrations_lock meta-tables track state. Set SET_MIGRATE_AGGREGATE_TABLE_TO_TRUE=1 to force the aggregate-table migration if it was interrupted.
2. System Requirements
2.1 Runtime
| Component | Requirement |
|---|---|
| Node.js | >= 20.4.0. Explicitly blocked: all versions below 18, and 20.0.0 through 20.3.x |
| npm | Bundled with Node.js |
| Operating System | Linux (recommended), macOS, Windows (with caveats for ping and system service monitors) |
| Architecture | amd64, arm64, armv7 (Docker multi-arch) |
| Memory | ~150 MB base + ~1 MB per active monitor (SQLite); MariaDB adds its own footprint |
| Disk | SQLite database grows with heartbeat retention; plan 1-5 GB for 100+ monitors over months |
Important
The Node.js version check in
server/server.jscallsprocess.exit(1)immediately if the runtime is below 18 or falls in the 20.0.x-20.3.x range. These versions have known bugs affecting SQLite bindings and DNS resolution. There is no override.
2.2 Optional Dependencies
| Feature | Dependency |
|---|---|
| Ping monitors | System ping command (or ping6 for IPv6) |
| Chromium/Real Browser monitors | Playwright-compatible Chromium install, or remote browser via remote_browser table |
| Game server monitors | gamedig npm package (bundled) |
| RADIUS monitors | node-radius-client (bundled) |
| SIP monitors | sipsak system binary |
| System service monitors | systemctl (Linux only) |
| SNMP monitors | net-snmp (bundled) |
| Embedded MariaDB | UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB=1 (downloads and manages a local MariaDB instance) |
| Cloudflare Tunnel | node-cloudflared-tunnel (bundled), no external binary needed |
2.3 Database Backend Selection
| Backend | When to Use |
|---|---|
| SQLite (default) | Single-instance deployments. Zero config. File-based at DATA_DIR/kuma.db. WAL mode enabled. |
| MariaDB | Multi-instance setups (future HA), large-scale deployments, or when you need a centralized DB. Set via UPTIME_KUMA_DB_TYPE=mariadb. |
Warning
PostgreSQL and MySQL (non-MariaDB) are not supported. The knex configuration in
server/setup-database.jsonly handlessqliteandmariadbclient types. Passing an unsupportedUPTIME_KUMA_DB_TYPEvalue causes a startup crash.
3. Configuration Reference
3.1 Environment Variables — Complete Reference
All configuration is done through environment variables. There is no configuration file. Variables are read at startup; changes require a restart.
Server & Network
| Variable | Default | Description |
|---|---|---|
UPTIME_KUMA_PORT |
3001 |
HTTP(S) listen port |
UPTIME_KUMA_HOST / HOST |
0.0.0.0 |
Bind address |
UPTIME_KUMA_SSL_KEY / SSL_KEY |
(none) | Path to TLS private key file (enables HTTPS) |
UPTIME_KUMA_SSL_CERT / SSL_CERT |
(none) | Path to TLS certificate file |
UPTIME_KUMA_SSL_KEY_PASSPHRASE / SSL_KEY_PASSPHRASE |
(none) | Passphrase for encrypted TLS key |
UPTIME_KUMA_WS_ORIGIN_CHECK |
cors-like |
WebSocket origin validation mode. Options: cors-like (default, checks Origin header against server URL), bypass (disables check) |
UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN |
(unset) | When set, removes X-Frame-Options: SAMEORIGIN header (required for embedding in iframes) |
Database
| Variable | Default | Description |
|---|---|---|
DATA_DIR |
./data/ |
Directory for SQLite database, TLS certs, and uploaded assets |
UPTIME_KUMA_DB_TYPE |
sqlite |
Database backend: sqlite or mariadb |
UPTIME_KUMA_DB_HOSTNAME |
(none) | MariaDB hostname |
UPTIME_KUMA_DB_PORT |
(none) | MariaDB port |
UPTIME_KUMA_DB_NAME |
(none) | MariaDB database name |
UPTIME_KUMA_DB_SOCKET |
(none) | MariaDB Unix socket path (alternative to host:port) |
UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS |
(knex default) | Maximum connections in the knex connection pool |
UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB |
(unset) | Enable embedded MariaDB server (downloads and manages a local instance) |
SQL_LOG |
(unset) | Enable SQL query logging to stdout |
SET_MIGRATE_AGGREGATE_TABLE_TO_TRUE |
(unset) | Force the aggregate table migration to re-run (recovery from interrupted migration) |
Important
When using MariaDB, all four connection variables (
DB_HOSTNAME,DB_PORT,DB_NAME, and eitherDB_SOCKETor host+port) must be set. The knex client ismysql2under the hood — connection strings are not supported, only discrete parameters.
Tunnel & Remote Access
| Variable | Default | Description |
|---|---|---|
UPTIME_KUMA_CLOUDFLARED_TOKEN |
(none) | Pre-configured Cloudflare Tunnel token for automatic tunnel startup |
UPTIME_KUMA_IS_CONTAINER |
(auto-detected) | Indicates container environment; affects Cloudflared binary path resolution |
Monitoring & Debugging
| Variable | Default | Description |
|---|---|---|
UPTIME_KUMA_LOG_FORMAT |
default |
Log output format. Used in src/util.js |
UPTIME_KUMA_HIDE_LOG |
(none) | Comma-separated log tags to suppress (test usage) |
UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID |
(none) | Monitor ID whose HTTP response bodies should be logged (debug only) |
UPTIME_KUMA_ALLOW_ALL_CHROME_EXEC |
(unset) | Allow custom Chromium executable paths for Real Browser monitors (security risk) |
TIMELOGGER |
(unset) | Enable performance timing logs in server/util-server.js |
DEBUG |
(unset) | Enable debug output for the API cache module |
Runtime
| Variable | Default | Description |
|---|---|---|
NODE_ENV |
production |
Node.js environment. Set to development for Vite dev server proxying |
NODE_TLS_REJECT_UNAUTHORIZED |
1 |
Set to 0 to disable TLS certificate verification globally (not recommended) |
TZ |
(system) | Timezone for the process. Read by server/uptime-kuma-server.js for display |
NOTIFICATION_PROXY / notification_proxy |
(none) | HTTP(S)/SOCKS proxy URL for all outbound notification requests |
3.2 In-App Settings (stored in setting table)
Beyond environment variables, many operational settings are stored in the setting key-value table and managed via the Socket.IO setSettings event. These include:
keepDataPeriodDays— Heartbeat retention period in days. Theclear-old-databackground job uses this to prune old heartbeats.entryPage— Default landing page:dashboardor a status page slug.searchEngineIndex— Whether to allow search engine indexing (controlsrobots.txt).primaryBaseURL— Base URL for badge and status page links.steamAPIKey— API key for Steam game server monitoring via GameDig.dnsCache— Enable/disable DNS result caching.tlsExpiryNotifyDays— Days before TLS certificate expiry to trigger alerts.trustProxy— Whether Express should trustX-Forwarded-*headers (required behind reverse proxies).checkUpdate— Enable update check notifications.
Tip
The
trustProxysetting is critical when running behind a reverse proxy (nginx, Traefik, Caddy). Without it, rate limiting and IP-based features see the proxy's IP instead of the client's. Set it via Settings > General in the UI or via thesetSettingsSocket.IO event.
3.3 Monitor Configuration Schema
The monitor table has 60+ columns. Key configuration fields per monitor:
| Column | Type | Default | Description |
|---|---|---|---|
type |
string | — | Monitor type identifier (e.g., http, ping, dns, docker, push, mqtt) |
active |
boolean | 1 |
Whether the monitor is running |
interval |
integer | 20 |
Check interval in seconds |
retry_interval |
integer | 0 |
Seconds between retries on failure (0 = same as interval) |
maxretries |
integer | 0 |
Number of retries before marking down |
upside_down |
boolean | 0 |
Invert status logic (UP becomes DOWN) |
url |
string | — | Target URL for HTTP/keyword/JSON monitors |
hostname |
string | — | Target hostname for TCP/ping/DNS/SMTP monitors |
port |
integer | — | Target port |
keyword |
string | — | Expected keyword in response body |
method |
string | GET |
HTTP method |
body |
text | — | HTTP request body |
headers |
text | — | HTTP request headers (JSON) |
basic_auth_user |
string | — | HTTP Basic Auth username |
basic_auth_pass |
string | — | HTTP Basic Auth password |
accepted_statuscodes_json |
text | ["200-299"] |
JSON array of accepted HTTP status codes/ranges |
maxredirects |
integer | 10 |
Maximum HTTP redirects to follow |
ignore_tls |
boolean | 0 |
Skip TLS certificate verification for this monitor |
proxy_id |
FK | — | Proxy to route checks through |
docker_host |
FK | — | Docker host for container monitors |
docker_container |
string | — | Docker container name/ID |
dns_resolve_type |
string | — | DNS record type (A, AAAA, CNAME, MX, NS, PTR, SOA, SRV, TXT, CAA) |
dns_resolve_server |
string | — | Custom DNS server for resolution |
push_token |
string | — | Unique token for push-type monitors |
conditions |
text | — | JSON condition expressions for conditional alerting |
resend_interval |
integer | 0 |
Seconds between re-sending notifications while down |
packet_size |
integer | 56 |
Ping packet size in bytes |
ip_family |
string | — | Force IPv4 or IPv6 |
mqtt_* |
various | — | MQTT-specific fields (topic, username, password, success message) |
database_* |
various | — | Database monitor fields (connection string, query) |
grpc_* |
various | — | gRPC fields (URL, metadata, body, method, service name, enable TLS) |
radius_* |
various | — | RADIUS fields (username, password, secret, called/calling station) |
oauth_* |
various | — | OAuth2 fields (client_id, client_secret, token_url, scopes, auth_method) |
tls_ca / tls_cert / tls_key |
text | — | Client TLS certificate material |
snmp_* |
various | — | SNMP fields (OID, community, version, auth/priv protocol) |
rabbitmq_* |
various | — | RabbitMQ node health fields |
kafka_* |
various | — | Kafka producer fields (brokers, topic, message, SASL, TLS) |
game |
string | — | Game identifier for GameDig monitors |
manual_status |
integer | — | Current status for Manual monitor type |
system_service_name |
string | — | systemd service name |
ping_numeric |
boolean | — | Use numeric output for ping |
ping_count |
integer | — | Number of ping packets per check |
ping_per_request_timeout |
integer | — | Ping timeout per packet |
Validation Constraints
Monitor.validate() enforces these constraints before save:
interval: bounded byMIN_INTERVAL_SECONDtoMAX_INTERVAL_SECONDretry_interval: same bounds as interval- Response body max length:
0toRESPONSE_BODY_LENGTH_MAXbytes - Screenshot delay (Real Browser): must be non-negative, less than 0.8x timeout, and less than 0.5x interval
- JSON fields (
kafkaProducerBrokers,kafkaProducerSaslOptions,rabbitmqNodes,conditions,headers,accepted_statuscodes): validated as parseable JSON
4. Deployment & Scaling
4.1 Docker Deployment (Recommended)
The official Dockerfile is at docker/dockerfile. Multi-arch builds support linux/amd64, linux/arm64, and linux/arm/v7.
docker run -d \
--name uptime-kuma \
--restart=unless-stopped \
-p 3001:3001 \
-v uptime-kuma-data:/app/data \
louislam/uptime-kuma:2
With SSL termination:
docker run -d \
--name uptime-kuma \
--restart=unless-stopped \
-p 3001:3001 \
-v uptime-kuma-data:/app/data \
-v /path/to/certs:/certs:ro \
-e UPTIME_KUMA_SSL_KEY=/certs/privkey.pem \
-e UPTIME_KUMA_SSL_CERT=/certs/fullchain.pem \
louislam/uptime-kuma:2
With MariaDB backend:
docker run -d \
--name uptime-kuma \
--restart=unless-stopped \
-p 3001:3001 \
-v uptime-kuma-data:/app/data \
-e UPTIME_KUMA_DB_TYPE=mariadb \
-e UPTIME_KUMA_DB_HOSTNAME=mariadb-host \
-e UPTIME_KUMA_DB_PORT=3306 \
-e UPTIME_KUMA_DB_NAME=uptime_kuma \
louislam/uptime-kuma:2
Development compose (docker/docker-compose-dev.yml):
services:
uptime-kuma:
build:
context: ..
dockerfile: docker/dockerfile
ports:
- "3001:3001"
volumes:
- ./data:/app/data
Docker Healthcheck
The container includes a compiled Go binary at extra/healthcheck.go that performs an HTTP GET to the configured host and port. It reads UPTIME_KUMA_HOST, UPTIME_KUMA_PORT, and SSL variables to construct the health URL. The Dockerfile's HEALTHCHECK directive calls this binary.
4.2 Bare-Metal Deployment
git clone https://github.com/louislam/uptime-kuma.git
cd uptime-kuma
npm run setup
# Installs production deps, builds frontend, sets up DB
node server/server.js
# Or use a process manager:
pm2 start server/server.js --name uptime-kuma
Tip
For production bare-metal, use
pm2orsystemdto manage the process. The application handlesSIGINTandSIGTERMfor graceful shutdown (30-second timeout viahttp-graceful-shutdown), so both process managers can stop it cleanly.
4.3 Reverse Proxy Configuration
Uptime Kuma requires WebSocket support from any reverse proxy. The critical path is the Socket.IO transport upgrade from HTTP to WebSocket.
nginx example:
server {
listen 443 ssl http2;
server_name status.example.com;
ssl_certificate /etc/letsencrypt/live/status.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/status.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Warning
Uptime Kuma does not support running under a subdirectory path (e.g.,
example.com/uptime/). It must be served at the root of its hostname. See GitHub issue #7279. The Express static file serving and Vue router both assume root-level mounting.
Traefik labels (Docker Compose):
labels:
- "traefik.enable=true"
- "traefik.http.routers.uptime-kuma.rule=Host(`status.example.com`)"
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
- "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
- "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"
After deploying behind a reverse proxy, enable trustProxy in Settings > General so Express reads X-Forwarded-* headers correctly.
4.4 Cloudflare Tunnel
Uptime Kuma has built-in Cloudflare Tunnel support via node-cloudflared-tunnel. Configuration options:
- UI-managed: Go to Settings > Reverse Proxy, enter your Cloudflare Tunnel token. The
cloudflared-socket-handlermanages the tunnel lifecycle. - Environment variable: Set
UPTIME_KUMA_CLOUDFLARED_TOKENfor automatic tunnel startup on boot.
The tunnel process is managed by UptimeKumaServer and is stopped during graceful shutdown.
4.5 Scaling Considerations
Uptime Kuma is architected as a single-instance application. The monitor heartbeat loop, in-memory monitor map, and Socket.IO session state are all process-local.
- Horizontal scaling is not supported with SQLite. Two instances writing to the same SQLite file will corrupt data.
- MariaDB enables read replicas but active-active write is not implemented — there is no distributed lock or leader election.
- Vertical scaling: increase
UPTIME_KUMA_DB_POOL_MAX_CONNECTIONSfor MariaDB when running hundreds of monitors.
For high availability, the recommended approach is active-passive with shared storage (e.g., a network filesystem for DATA_DIR with SQLite, or a shared MariaDB instance) and an external failover mechanism.
5. Operations
5.1 Startup Sequence
When server/server.js starts:
- Node.js version check — exits if below 18 or in 20.0.x-20.3.x range
UptimeKumaServer.getInstance()— singleton initialization- Database setup (
server/setup-database.js) — selects SQLite or MariaDB based onUPTIME_KUMA_DB_TYPE - Knex migrations — runs all pending migrations from
db/knex_migrations/ - Express middleware setup — security headers, rate limiting, CORS, static files
- Socket.IO server creation with origin check policy
- Socket handler registration — all 10 handlers attach their event listeners
- HTTP(S) server starts listening on configured host:port
needSetupcheck — if no user exists, the UI shows the setup wizard- Background jobs start (
clear-old-data,incremental-vacuum) - All active monitors begin their heartbeat loops
- Cloudflare tunnel starts if token is configured
5.2 Graceful Shutdown
Shutdown is managed by http-graceful-shutdown with a 30-second timeout. On SIGINT or SIGTERM:
- HTTP server stops accepting new connections
- All active monitors stop their heartbeat loops
- Cloudflare tunnel process is killed
- Background jobs are stopped
- Database connection is closed
- Process exits
Note
If monitors do not stop within the 30-second window, the process force-exits. Long-running checks (Real Browser monitors with high timeouts, slow database monitors) may be interrupted mid-check. The next startup will resume from the last recorded heartbeat.
5.3 Background Jobs
Clear Old Data (server/jobs/clear-old-data.js)
Periodically deletes heartbeat records older than keepDataPeriodDays (configured in Settings). Runs on a timer after startup. Prevents unbounded database growth.
Incremental Vacuum (server/jobs/incremental-vacuum.js)
SQLite-only. Runs PRAGMA incremental_vacuum periodically to reclaim free pages without the full-table lock of VACUUM. Only active when UPTIME_KUMA_DB_TYPE is sqlite.
Tip
For large SQLite databases (multiple GB), the incremental vacuum is far less disruptive than a full
VACUUM. If you need to reclaim significant space after a bulk heartbeat purge, you can trigger a full vacuum via the database socket handler's shrink function in the UI (Settings > Info > Shrink Database).
5.4 Backup and Restore
SQLite
The database file is at DATA_DIR/kuma.db (with WAL files kuma.db-wal and kuma.db-shm). To back up:
# Stop Uptime Kuma first for a consistent backup
cp data/kuma.db data/kuma.db.bak
cp data/kuma.db-wal data/kuma.db-wal.bak 2>/dev/null
cp data/kuma.db-shm data/kuma.db-shm.bak 2>/dev/null
For online backup, use the SQLite .backup command or sqlite3 data/kuma.db ".backup 'backup.db'".
The UI also provides an Export function that creates a JSON export of monitors, notifications, and status pages (accessible via Socket.IO exportBackup event).
MariaDB
Use standard mysqldump against the configured database:
mysqldump -h "$UPTIME_KUMA_DB_HOSTNAME" -P "$UPTIME_KUMA_DB_PORT" \
"$UPTIME_KUMA_DB_NAME" > backup.sql
5.5 Monitoring Uptime Kuma Itself
Prometheus Metrics
The /metrics endpoint (requires HTTP Basic Auth) exposes:
- Standard Node.js process metrics via
prom-client(CPU, memory, event loop lag, GC) - HTTP request metrics via
prometheus-api-metrics(request count, duration histograms by route) - Custom monitor metrics (if configured)
Prometheus scrape config:
scrape_configs:
- job_name: 'uptime-kuma'
basic_auth:
username: '<uptime-kuma-username>'
password: '<uptime-kuma-password>'
static_configs:
- targets: ['status.example.com:3001']
Healthcheck Endpoint
The Go-compiled healthcheck binary (extra/healthcheck.go) performs an HTTP(S) GET to the server. Docker's HEALTHCHECK directive uses this. For external health monitoring:
curl -f http://localhost:3001/api/entry-page || echo "DOWN"
5.6 Log Management
Uptime Kuma logs to stdout/stderr. In Docker, use standard Docker log drivers:
docker logs uptime-kuma --tail 100 -f
Controlling log output:
| Variable | Effect |
|---|---|
UPTIME_KUMA_LOG_FORMAT |
Changes log output format |
UPTIME_KUMA_HIDE_LOG |
Comma-separated tags to suppress |
SQL_LOG |
Enables SQL query logging (very verbose) |
TIMELOGGER |
Enables performance timing for operations |
DEBUG |
Enables API cache debug output |
UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID |
Logs full HTTP response body for a specific monitor (debug) |
Note
There is no built-in log rotation. When running with Docker, configure the Docker logging driver (e.g.,
json-filewithmax-sizeandmax-file). For bare-metal deployments, pipe stdout throughlogrotateor a similar tool.
5.7 Database Maintenance
SQLite WAL Mode
SQLite runs in WAL (Write-Ahead Logging) mode by default. This allows concurrent reads during writes but creates .wal and .shm companion files. Never delete these files while the server is running.
Connection Pool Tuning (MariaDB)
For deployments with hundreds of monitors, each performing checks at different intervals, the connection pool can become a bottleneck. Symptoms include KnexTimeoutError: Knex: Timeout acquiring a connection (see GitHub issues #7260 and #6486).
Increase the pool size:
UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS=20
Migration Recovery
If a migration fails mid-execution (particularly the aggregate table migration referenced in issue #7274), the knex_migrations_lock table may remain locked:
-- Check lock state
SELECT * FROM knex_migrations_lock;
-- Unlock if stuck (only when Uptime Kuma is stopped)
UPDATE knex_migrations_lock SET is_locked = 0;
For the specific convert-float-precision migration failure (issue #6920), check that the database has sufficient disk space and that no other process holds a lock on the database file.
6. Performance
6.1 Monitor Heartbeat Loop
Each active monitor runs an independent setInterval loop in the Node.js event loop. The check sequence per heartbeat:
- Resolve proxy (if configured)
- Execute the monitor-type-specific check (HTTP request, TCP connect, ping, DNS query, etc.)
- Evaluate conditions (if configured)
- Record heartbeat to database
- Calculate uptime statistics via
UptimeCalculator - Emit status update to all connected Socket.IO clients
- Trigger notifications if status changed
- Sleep until next interval
Performance Impact by Monitor Type
| Monitor Type | Relative Cost | Notes |
|---|---|---|
| Ping | Low | Spawns system ping process |
| TCP | Low | Single TCP connect + optional TLS handshake |
| HTTP(S) | Low-Medium | Full HTTP request; response body limited by RESPONSE_BODY_LENGTH_MAX |
| DNS | Low | Single DNS query via dns.resolve |
| Push | Negligible | Passive — waits for incoming HTTP push |
| Docker | Low | Docker API call over socket/HTTP |
| MQTT | Medium | Establishes MQTT connection, subscribes, waits for message |
| Database (MySQL/PostgreSQL/etc.) | Medium | Opens connection, runs query, closes |
| Real Browser | High | Launches headless Chromium, navigates page, optional screenshot |
| GameDig | Medium | Game-specific protocol query |
| gRPC | Medium | gRPC health check or keyword match |
| SNMP | Low | Single SNMP GET request |
6.2 UptimeCalculator
server/uptime-calculator.js maintains per-monitor statistics in three time granularities:
- Minutely (
stat_minutely): aggregated every minute - Hourly (
stat_hourly): aggregated every hour - Daily (
stat_daily): aggregated every day
These aggregations run in the heartbeat loop and write to the database. The calculator also maintains in-memory caches for real-time dashboard updates. For large monitor counts, the aggregation writes can become a database write bottleneck — this is one of the primary drivers for considering MariaDB over SQLite at scale.
6.3 API Response Caching
Badge endpoints (/api/badge/:id/*) use a 5-minute server-side cache (via the apicache module). This prevents badge consumers (GitHub READMEs, status dashboards) from generating excessive database load.
6.4 Tuning Recommendations
| Scenario | Recommendation |
|---|---|
| 50+ monitors | Increase interval for non-critical monitors to 60-300s |
| Real Browser monitors | Use remote browsers (remote_browser table) to offload Chromium from the main process |
| Large heartbeat tables | Set keepDataPeriodDays to limit retention (e.g., 90 or 180 days) |
| Slow dashboard loads | Reduce the number of monitors displayed per status page group |
| MariaDB latency | Place MariaDB on the same host or low-latency network link |
7. Security
7.1 Authentication Model
Initial Setup
On first launch with no users in the database, the /setup page is presented. The first user created becomes the admin. After setup, the setup page is disabled (the needSetup Socket.IO event returns false).
Login Flow
- Client emits
loginwith username and password - Server verifies password via
bcryptjs.compare() - If 2FA is enabled, server responds with
tokenRequired: true - Client emits
loginByTokenor submits TOTP code viaverifyToken - Server issues a JWT token (via
jsonwebtoken) returned to the client - Client stores token and uses
loginByTokenfor subsequent connections
Two-Factor Authentication (2FA)
TOTP-based 2FA using notp and thirty-two libraries:
prepare2FA— generates a TOTP secret and QR code URIsave2FA— verifies a TOTP token and persists the secret to the user recorddisable2FA— removes the TOTP secret after password verification
Caution
There is no recovery code mechanism in the source. If a user loses their 2FA device and cannot produce a valid TOTP code, the only recovery path is direct database modification: clear the
twofa_secretandtwofa_statuscolumns in theusertable. This requires stopping the server and directly editing the SQLite/MariaDB database.
API Key Authentication
API keys are managed via the api-key-socket-handler. Keys are stored in the api_key table with:
- A hashed key value
- An optional expiry date
- An active/inactive flag
API keys authenticate requests to the /metrics endpoint and can be used for push monitor endpoints. They are verified in server/auth.js.
7.2 WebSocket Security
Origin Check
The UPTIME_KUMA_WS_ORIGIN_CHECK variable controls Socket.IO origin validation:
cors-like(default): checks that theOriginheader matches the server's own URL. Blocks cross-origin WebSocket connections.bypass: disables origin checking entirely. Required in some reverse proxy setups where the origin header is rewritten.
Warning
Setting
UPTIME_KUMA_WS_ORIGIN_CHECK=bypassdisables CSRF protection for WebSocket connections. Only use this when the reverse proxy enforces origin validation at its layer, or when the deployment is on a trusted internal network.
Frame Protection
The X-Frame-Options: SAMEORIGIN header is set by default, preventing the UI from being embedded in cross-origin iframes (clickjacking protection). Set UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN to allow embedding.
7.3 Password Security
- Passwords are hashed with
bcryptjsbefore storage - Password changes require the current password (via
changePasswordSocket.IO event) - The
/.well-known/change-passwordendpoint exists for browser password-manager integration
7.4 TLS Configuration
Server-Side TLS
Set UPTIME_KUMA_SSL_KEY and UPTIME_KUMA_SSL_CERT to enable HTTPS directly. The passphrase for encrypted keys is provided via UPTIME_KUMA_SSL_KEY_PASSPHRASE.
Monitor Client TLS
Individual monitors can configure client TLS certificates for mTLS scenarios:
tls_ca— Custom CA certificatetls_cert— Client certificatetls_key— Client private key
The ignore_tls flag per monitor disables TLS verification for that specific check.
Warning
Issue #7265 documents a case where a monitor reports UP when TCP connects successfully but TLS negotiation fails. The
expected_tls_alertfield was added to handle explicit TLS alert expectations, but the default behavior is to report UP on successful TCP connect regardless of TLS state whenignore_tlsis true.
7.5 Proxy Support
Proxies are defined in the proxy table and assigned per-monitor via proxy_id. Supported proxy types include HTTP(S) and SOCKS proxies. This allows monitors to check targets through specific network paths or to anonymize the source IP.
The NOTIFICATION_PROXY environment variable sets a global proxy for all outbound notification requests (separate from monitor check proxies).
7.6 Rate Limiting
Express rate limiting middleware is applied to prevent brute-force attacks on the login endpoint and API abuse. The specific rate limits are configured in server/server.js middleware setup.
7.7 Security Headers
The Express app sets standard security headers:
X-Frame-Options: SAMEORIGIN(unless disabled)X-Content-Type-Options: nosniffX-XSS-Protectionheaders- Content Security Policy headers appropriate for the SPA
7.8 Notification Security
Notification provider configurations (API keys, webhook URLs, SMTP credentials) are stored as JSON in the notification table's config column. These are only accessible to authenticated users via Socket.IO events.
The notification_sent_history table provides deduplication, preventing notification storms when a monitor flaps between UP and DOWN states.
8. Developer Internals
8.1 Repository Structure
uptime-kuma/
├── server/ Backend (Node.js + Express)
│ ├── server.js Entry point, Express + Socket.IO setup
│ ├── uptime-kuma-server.js Singleton lifecycle manager
│ ├── database.js Database abstraction (SQLite/MariaDB)
│ ├── auth.js Authentication middleware
│ ├── notification.js Notification dispatcher
│ ├── prometheus.js Prometheus metrics export
│ ├── uptime-calculator.js Statistics aggregation engine
│ ├── config.js SSL and config helpers
│ ├── setup-database.js DB type selection and connection setup
│ ├── util-server.js Server-side utilities
│ ├── model/ ORM models (13 classes)
│ │ ├── monitor.js Core monitor model + heartbeat loop
│ │ ├── heartbeat.js Heartbeat data model
│ │ ├── status_page.js Status page model
│ │ ├── maintenance.js Maintenance window model
│ │ ├── tag.js Tag model
│ │ ├── user.js User model
│ │ ├── api_key.js API key model
│ │ ├── docker_host.js Docker host model
│ │ ├── domain_expiry.js Domain expiry tracking
│ │ ├── group.js Monitor group model
│ │ ├── incident.js Incident model
│ │ ├── proxy.js Proxy model
│ │ └── remote_browser.js Remote browser model
│ ├── monitor-types/ Monitor type plugins (23 types)
│ │ ├── dns.js DNS resolution monitor
│ │ ├── gamedig.js Game server monitor
│ │ ├── globalping.js Globalping API monitor
│ │ ├── group.js Group (aggregate) monitor
│ │ ├── grpc.js gRPC health/keyword monitor
│ │ ├── manual.js Manual status monitor
│ │ ├── mongodb.js MongoDB monitor
│ │ ├── mqtt.js MQTT subscribe monitor
│ │ ├── mssql.js MSSQL monitor
│ │ ├── mysql.js MySQL monitor
│ │ ├── oracledb.js Oracle DB monitor
│ │ ├── postgres.js PostgreSQL monitor
│ │ ├── rabbitmq.js RabbitMQ node health monitor
│ │ ├── real-browser-monitor-type.js Chromium headless monitor
│ │ ├── redis.js Redis monitor
│ │ ├── sip-options.js SIP OPTIONS monitor
│ │ ├── smtp.js SMTP monitor
│ │ ├── snmp.js SNMP GET monitor
│ │ ├── system-service.js systemd service monitor
│ │ ├── tailscale-ping.js Tailscale DERP ping monitor
│ │ ├── tcp.js TCP/TLS monitor
│ │ └── websocket-upgrade.js WebSocket upgrade monitor
│ ├── monitor-conditions/ Condition evaluation system
│ │ ├── evaluator.js Expression group evaluator
│ │ ├── expression.js Single condition expression
│ │ └── operators/ 16 operator implementations
│ ├── notification-providers/ 93 notification provider plugins
│ ├── socket-handlers/ 10 Socket.IO event handler modules
│ ├── jobs/ Background job definitions
│ ├── routers/ Express route handlers
│ ├── modules/ Vendored/modified modules (apicache)
│ └── analytics/ Analytics integration (GA, Matomo, Plausible, Umami)
├── src/ Frontend (Vue 3 SPA)
│ ├── main.js Vue app entry point
│ ├── App.vue Root component
│ ├── components/ Vue components
│ ├── pages/ Page-level Vue components
│ ├── mixins/ Vue mixins (socket, theme, mobile)
│ ├── router.js Vue Router configuration
│ ├── util.js Shared utilities (also used by backend)
│ └── languages/ 77 i18n locale files
├── db/
│ ├── knex_init_db.js Schema definitions (23 tables)
│ └── knex_migrations/ 48 migration files
├── docker/
│ ├── dockerfile Production Dockerfile
│ ├── docker-compose-dev.yml Dev compose
│ ├── debian-base.dockerfile Base image
│ └── builder-go.dockerfile Go healthcheck builder
├── extra/
│ ├── healthcheck.go Docker healthcheck binary
│ ├── healthcheck.js Node.js healthcheck (legacy)
│ └── push-examples/ Push monitor integration examples
├── config/
│ └── vite.config.js Vite build configuration
└── test/ Test suite
└── backend-test/ Backend tests + e2e (Playwright)
8.2 Model Layer (redbean-node ORM)
All 13 models extend the redbean-node Bean class. The Monitor model is by far the most complex, containing:
- The heartbeat loop (
start()method) — an async loop that calls the appropriateMonitorType.check()and records results validate()— comprehensive input validation- Status transition logic (UP/DOWN/PENDING/MAINTENANCE)
- Notification dispatch on state changes
- Certificate expiry tracking for TLS monitors
8.3 Monitor Type Plugin System
Each monitor type extends the MonitorType base class and implements:
class MyMonitorType extends MonitorType {
name = "my-type"; // Unique identifier
supportsConditions = true; // Whether conditions system applies
/**
* @param {Monitor} monitor - The monitor instance
* @param {Heartbeat} heartbeat - The heartbeat to populate
* @param {object} tlsInfo - TLS certificate info (if applicable)
* @returns {Promise<void>}
*/
async check(monitor, heartbeat, tlsInfo) {
// Perform the check
// Set heartbeat.status (UP=1, DOWN=0)
// Set heartbeat.msg (status message)
// Set heartbeat.ping (response time in ms)
}
// Optional: declare condition variables for the conditions system
get conditionVariables() {
return [
{ id: "response_body", name: "Response Body", type: "string" },
{ id: "status_code", name: "Status Code", type: "number" },
];
}
}
Built-in types (handled directly in Monitor.check() without a separate class): HTTP(S), Keyword, JSON Query, Ping, Docker Container, Push, TLS Certificate.
Plugin types (23 classes in server/monitor-types/):
| Type | Class | Key Behavior |
|---|---|---|
dns |
DnsMonitorType |
Resolves DNS records (A/AAAA/CNAME/MX/NS/PTR/SOA/SRV/TXT/CAA), compares against expected value |
gamedig |
GameDigMonitorType |
Queries game servers using the gamedig library, reports player count |
globalping |
GlobalpingMonitorType |
Runs measurements via the Globalping API from distributed probes |
group |
GroupMonitorType |
Aggregates status of child monitors; UP only if all children are UP |
grpc |
GrpcKeywordMonitorType |
gRPC health check or keyword match in response |
manual |
ManualMonitorType |
No automatic checks; status set manually via API |
mongodb |
MongodbMonitorType |
Connects to MongoDB, optionally runs a command/query |
mqtt |
MqttMonitorType |
Connects to MQTT broker, subscribes to topic, checks for expected message |
mssql |
MssqlMonitorType |
Connects to Microsoft SQL Server, runs query |
mysql |
MysqlMonitorType |
Connects to MySQL/MariaDB, runs query |
oracledb |
OracleDbMonitorType |
Connects to Oracle Database, runs query |
postgres |
PostgresMonitorType |
Connects to PostgreSQL, runs query |
rabbitmq |
RabbitMqMonitorType |
Checks RabbitMQ node health via management API |
real-browser |
RealBrowserMonitorType |
Launches Chromium via Playwright, navigates to URL, optional screenshot |
redis |
RedisMonitorType |
Connects to Redis, runs PING or custom command |
sip-options |
SIPMonitorType |
Sends SIP OPTIONS request via sipsak binary |
smtp |
SMTPMonitorType |
Connects to SMTP server, checks response |
snmp |
SNMPMonitorType |
SNMP GET (v1/v2c/v3 with auth/priv protocols) |
system-service |
SystemServiceMonitorType |
Checks systemd service status via systemctl |
tailscale-ping |
TailscalePing |
Pings a Tailscale node via DERP relay |
tcp |
TCPMonitorType |
TCP connect with optional TLS upgrade |
websocket-upgrade |
WebSocketMonitorType |
Checks WebSocket upgrade handshake |
8.4 Monitor Conditions System
The conditions system (server/monitor-conditions/) allows monitors to define complex evaluation rules beyond simple UP/DOWN:
Architecture:
ConditionExpressionGroup— AND/OR groups of conditionsConditionExpression— single condition:variableoperatorvalue- 16 operators in
server/monitor-conditions/operators/:
| Operator Class | Behavior |
|---|---|
StringEquals / StringNotEquals |
Exact string comparison |
Contains / NotContains |
Substring match |
StartsWith / NotStartsWith |
Prefix match |
EndsWith / NotEndsWith |
Suffix match |
NumberEquals / NumberNotEquals |
Numeric equality |
LessThan / GreaterThan |
Numeric comparison |
LessThanOrEqual / GreaterThanOrEqual |
Numeric comparison |
Each monitor type declares its conditionVariables — the set of runtime values available for condition evaluation. For HTTP monitors, this includes status_code, response_body, response_time. For DNS monitors, resolved_address. Each type exposes the variables relevant to its check results.
8.5 Notification Provider Architecture
All 93 notification providers extend NotificationProvider:
class MyProvider extends NotificationProvider {
name = "my-provider";
/**
* @param {object} notification - Provider config (from notification table)
* @param {string} msg - Notification message
* @param {Monitor} monitorJSON - Monitor data
* @param {Heartbeat} heartbeatJSON - Heartbeat data
* @returns {Promise<string>} - Success message
*/
async send(notification, msg, monitorJSON, heartbeatJSON) {
// Send the notification
// Return success message or throw on failure
}
}
The Notification dispatcher (server/notification.js) manages:
- Provider registration and lookup
- Sending notifications to all channels linked to a monitor
- Notification deduplication via
notification_sent_history - Error handling — a failing notification provider does not block the monitor loop
Notification requests route through NOTIFICATION_PROXY if set.
Caution
Issue #4058 documents cases where a notification provider timeout can freeze the monitor's heartbeat loop. The notification dispatch runs in the same async context as the heartbeat. If a provider's HTTP request hangs beyond the configured timeout, the monitor's next check is delayed. The
resend_intervalfield mitigates notification storms but does not address provider timeouts.
8.6 Socket.IO Event Reference
Socket.IO is the primary API. All events require authentication (JWT token via loginByToken) unless marked as public.
Authentication Events
| Event | Direction | Parameters | Description |
|---|---|---|---|
login |
Client → Server | username, password, token (2FA) |
Authenticate with credentials |
loginByToken |
Client → Server | token (JWT) |
Re-authenticate with stored token |
logout |
Client → Server | — | Invalidate session |
needSetup |
Client → Server | — | Check if initial setup is required (public) |
setup |
Client → Server | username, password |
Create initial admin account (only when no users exist) |
Monitor Management
| Event | Direction | Parameters | Description |
|---|---|---|---|
add |
Client → Server | Monitor config object | Create a new monitor |
editMonitor |
Client → Server | Monitor config object | Update an existing monitor |
deleteMonitor |
Client → Server | monitorID |
Delete a monitor and its heartbeats |
getMonitor |
Client → Server | monitorID |
Get a single monitor's config |
getMonitorList |
Server → Client | — | Pushed on connection: all monitors |
pauseMonitor |
Client → Server | monitorID |
Stop a monitor's heartbeat loop |
resumeMonitor |
Client → Server | monitorID |
Start a monitor's heartbeat loop |
Heartbeat & Statistics
| Event | Direction | Parameters | Description |
|---|---|---|---|
getMonitorBeats |
Client → Server | monitorID, period |
Get heartbeat history |
monitorImportantHeartbeatListCount |
Client → Server | monitorID |
Count of important heartbeats |
monitorImportantHeartbeatListPaged |
Client → Server | monitorID, offset, count |
Paginated important heartbeats |
clearEvents |
Client → Server | monitorID |
Clear important heartbeats |
clearHeartbeats |
Client → Server | monitorID |
Clear all heartbeats |
clearStatistics |
Client → Server | — | Clear all statistics |
heartbeatList |
Server → Client | monitorID, data, overwrite |
Pushed: heartbeat data updates |
heartbeat |
Server → Client | data |
Pushed: single new heartbeat |
importantHeartbeatList |
Server → Client | monitorID, data, overwrite |
Pushed: important heartbeat updates |
Tags
| Event | Direction | Parameters | Description |
|---|---|---|---|
getTags |
Client → Server | — | List all tags |
addTag |
Client → Server | tag object |
Create a tag |
editTag |
Client → Server | tag object |
Update a tag |
deleteTag |
Client → Server | tagID |
Delete a tag |
addMonitorTag |
Client → Server | tagID, monitorID, value |
Link a tag to a monitor |
editMonitorTag |
Client → Server | tagID, monitorID, value |
Update tag-monitor link |
deleteMonitorTag |
Client → Server | tagID, monitorID, value |
Remove tag-monitor link |
Notifications
| Event | Direction | Parameters | Description |
|---|---|---|---|
addNotification |
Client → Server | Notification config | Create notification channel |
deleteNotification |
Client → Server | notificationID |
Delete notification channel |
testNotification |
Client → Server | Notification config | Send a test notification |
checkApprise |
Client → Server | — | Check if Apprise is installed |
Settings
| Event | Direction | Parameters | Description |
|---|---|---|---|
getSettings |
Client → Server | — | Get all settings |
setSettings |
Client → Server | Settings object | Update settings |
changePassword |
Client → Server | currentPassword, newPassword |
Change user password |
Two-Factor Authentication
| Event | Direction | Parameters | Description |
|---|---|---|---|
prepare2FA |
Client → Server | — | Generate 2FA secret and QR URI |
save2FA |
Client → Server | token (TOTP code) |
Verify and save 2FA |
disable2FA |
Client → Server | password |
Disable 2FA |
verifyToken |
Client → Server | token, password |
Verify TOTP during login |
twoFAStatus |
Client → Server | — | Check 2FA status |
Maintenance (via maintenance-socket-handler)
| Event | Direction | Description |
|---|---|---|
addMaintenance |
Client → Server | Create maintenance window |
editMaintenance |
Client → Server | Update maintenance window |
deleteMaintenance |
Client → Server | Delete maintenance window |
getMaintenance |
Client → Server | Get maintenance details |
getMaintenanceList |
Client → Server | List all maintenance windows |
pauseMaintenance |
Client → Server | Pause a maintenance window |
resumeMaintenance |
Client → Server | Resume a maintenance window |
getMonitorMaintenance |
Client → Server | Get maintenance for a monitor |
Status Page (via status-page-socket-handler)
| Event | Direction | Description |
|---|---|---|
addStatusPage |
Client → Server | Create status page |
deleteStatusPage |
Client → Server | Delete status page |
saveStatusPage |
Client → Server | Update status page config |
getStatusPage |
Client → Server | Get status page details |
postIncident |
Client → Server | Create/update incident |
unpinIncident |
Client → Server | Remove pinned incident |
Docker Host (via docker-socket-handler)
| Event | Direction | Description |
|---|---|---|
addDockerHost |
Client → Server | Add Docker host connection |
editDockerHost |
Client → Server | Update Docker host |
deleteDockerHost |
Client → Server | Delete Docker host |
testDockerHost |
Client → Server | Test Docker host connectivity |
Other Handlers
| Handler | Key Events |
|---|---|
proxy-socket-handler |
addProxy, editProxy, deleteProxy |
database-socket-handler |
getDatabaseSize, shrinkDatabase |
cloudflared-socket-handler |
installCloudflared, startCloudflared, stopCloudflared, checkCloudflared |
remote-browser-socket-handler |
addRemoteBrowser, editRemoteBrowser, deleteRemoteBrowser, testRemoteBrowser |
api-key-socket-handler |
addAPIKey, deleteAPIKey, getAPIKeyList |
chart-socket-handler |
getChartData |
8.7 REST API Reference
REST endpoints are secondary to Socket.IO but serve critical public-facing functions.
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/ |
None | Serves the Vue SPA |
GET |
/setup-database-info |
None | Returns database type info for setup wizard |
GET |
/robots.txt |
None | Dynamic robots.txt based on searchEngineIndex setting |
GET |
/metrics |
Basic Auth | Prometheus metrics scrape endpoint |
GET |
/.well-known/change-password |
None | Redirects to password change (browser integration) |
ALL |
/api/push/:pushToken |
Push Token | Push monitor heartbeat receiver (GET or POST) |
GET |
/api/entry-page |
None | Returns configured landing page |
GET |
/api/badge/:id/status |
None | SVG status badge (5min cache) |
GET |
/api/badge/:id/uptime/:duration? |
None | SVG uptime percentage badge |
GET |
/api/badge/:id/ping/:duration? |
None | SVG average ping badge |
GET |
/api/badge/:id/avg-response/:duration? |
None | SVG average response time badge |
GET |
/api/badge/:id/cert-exp |
None | SVG certificate expiry badge |
GET |
/api/badge/:id/response |
None | SVG latest response time badge |
GET |
/status/:slug |
None | Public status page HTML |
GET |
/status/:slug/rss |
None | Status page RSS feed |
GET |
/status |
None | Status page index |
GET |
/status-page |
None | Status page redirect |
GET |
/api/status-page/:slug |
None | Status page JSON data |
GET |
/api/status-page/heartbeat/:slug |
None | Status page heartbeat data |
GET |
/api/status-page/:slug/manifest.json |
None | PWA manifest for status page |
GET |
/api/status-page/:slug/incident-history |
None | Status page incident history |
GET |
/api/status-page/:slug/badge |
None | Status page aggregate badge |
8.8 Frontend Architecture
The frontend is a Vue 3 SPA built with Vite 5.4:
- Entry:
src/main.js→src/App.vue - Routing:
src/router.js(Vue Router) - State: Socket.IO-driven reactivity (no Vuex/Pinia — state lives in the socket connection and component data)
- Styling: Bootstrap 5.1 with RTL support via PostCSS
- i18n:
vue-i18nwith 77 language files insrc/languages/ - Build:
vite buildcompiles to static files served by Express
Analytics Integration
Four analytics providers are supported via server/analytics/:
| Provider | Integration |
|---|---|
| Google Analytics | Script injection |
| Matomo | Script injection |
| Plausible | Script injection |
| Umami | Script injection |
Configured via the UI settings, not environment variables.
8.9 Testing
| Type | Location | Framework |
|---|---|---|
| Backend unit/integration | test/backend-test/ |
Custom test runner |
| End-to-end | test/ |
Playwright |
| Container tests | test/ |
Testcontainers |
Run backend tests:
npm run test-backend
Run e2e tests (requires Playwright browsers installed):
npx playwright test
9. Integration Points
9.1 Push Monitor API
Push monitors provide an inbound heartbeat mechanism for external services. Each push monitor has a unique push_token. External systems call:
# Simple heartbeat (UP)
curl "https://status.example.com/api/push/<push_token>?status=up&msg=OK&ping=50"
# Report DOWN
curl "https://status.example.com/api/push/<push_token>?status=down&msg=Service%20failed"
Parameters:
| Parameter | Default | Description |
|---|---|---|
status |
up |
up or down |
msg |
OK |
Status message |
ping |
(none) | Response time in ms |
Both GET and POST methods are accepted on the /api/push/:pushToken endpoint.
Push examples for various languages and tools are provided in extra/push-examples/.
9.2 Status Page Embedding
Status pages are publicly accessible at /status/:slug. Each status page supports:
- Custom title, description, and logo
- Monitor groups with display ordering
- Pinned incidents with markdown content
- Maintenance window display
- RSS feed at
/status/:slug/rss - PWA manifest at
/api/status-page/:slug/manifest.json - Aggregate badge at
/api/status-page/:slug/badge
9.3 Badge Integration
Badges are SVG images suitable for GitHub READMEs, dashboards, and documentation:





The :duration parameter is in hours. Badges are cached for 5 minutes server-side.
9.4 Prometheus Integration
The /metrics endpoint exports:
- Node.js process metrics: CPU usage, memory (RSS, heap), event loop lag, active handles/requests, GC statistics
- HTTP metrics: Request count by method/route/status, response time histograms, active connections
- Custom metrics: Available for extension via
server/prometheus.js
Authentication is required (HTTP Basic Auth with Uptime Kuma credentials).
9.5 Docker Integration
Docker host connections are stored in the docker_host table. Supported connection methods:
- Unix socket:
/var/run/docker.sock(default for same-host Docker) - TCP:
tcp://docker-host:2375(unencrypted) - TLS:
tcp://docker-host:2376with TLS certificates
Docker monitors check container status via the Docker API. The docker-socket-handler manages CRUD for host connections.
9.6 Cloudflare Tunnel Integration
The built-in Cloudflare Tunnel support (node-cloudflared-tunnel) provides:
- Zero-config remote access without port forwarding
- Managed via the
cloudflared-socket-handler(install, start, stop, check status) - Automatic startup via
UPTIME_KUMA_CLOUDFLARED_TOKENenvironment variable - Tunnel process lifecycle managed by
UptimeKumaServersingleton
9.7 Remote Browser Integration
For Real Browser monitors, Playwright can connect to remote browser instances:
- Remote browsers are stored in the
remote_browsertable - Connection via WebSocket URL to a Playwright-compatible browser service
- Managed via
remote-browser-socket-handler(add, edit, delete, test)
This offloads the heavy Chromium process from the Uptime Kuma server to dedicated browser infrastructure.
9.8 Notification Provider Categories
The 93 notification providers cover these categories:
| Category | Examples |
|---|---|
| Chat/Messaging | Slack, Discord, Telegram, Microsoft Teams, Mattermost, Rocket.Chat, Matrix, Google Chat, Line, Feishu, DingTalk, WeChat |
| SMTP, SendGrid, Mailgun | |
| Incident Management | PagerDuty, Opsgenie, VictorOps/Splunk On-Call, Squadcast, Incident.io, Grafana OnCall, ZenDuty |
| Push Notifications | Pushover, Pushbullet, Ntfy, Gotify, Bark, SimplePush |
| Webhooks | Generic Webhook, Custom POST, FlashDuty |
| SMS | Twilio, Vonage, SMSEagle, PromoSMS, ClickSend, SMSManager |
| Status Pages | Atlassian Statuspage, Instatus, Sorry |
| Monitoring | Datadog, New Relic, Dynatrace, Better Stack, Sentry |
| Home Automation | Home Assistant, IFTTT, Apprise |
9.9 Troubleshooting — Known Issues
| Issue | Symptom | Resolution |
|---|---|---|
| #7260 | KnexTimeoutError: Timeout acquiring a connection |
Increase UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS. Reduce concurrent monitor count or increase check intervals. |
| #6486 | KnexTimeoutError: pool full on MariaDB |
Same as above. Check for long-running queries blocking the pool. |
| #6920 | Migration fails on convert-float-precision |
Ensure sufficient disk space. Check no other process holds a DB lock. May need manual migration state cleanup. |
| #7274 | Aggregate table migration interrupted, stuck on restart | Set SET_MIGRATE_AGGREGATE_TABLE_TO_TRUE=1 to force re-run, or manually unlock knex_migrations_lock. |
| #7279 | Cannot run under a subdirectory path | Not supported. Must deploy at the root of a (sub)domain. Use a reverse proxy with a dedicated hostname. |
| #7265 | Monitor shows UP when TCP connects but TLS fails | Set ignore_tls=false and configure expected_tls_alert if you need to validate TLS negotiation. |
| #4058 | Notification provider timeout freezes monitor heartbeat | No built-in fix. Consider reducing notification provider timeouts at the provider level, or using faster notification channels. |
| #3574 | Ping fails with packet size below 16 bytes | Set packet_size to at least 16 (default is 56). Some OS implementations reject very small packets. |
Warning
The
knex_migrations_locktable can become permanently locked if the process crashes during a migration. Always checkSELECT * FROM knex_migrations_lockif Uptime Kuma fails to start after an unexpected shutdown. Unlock withUPDATE knex_migrations_lock SET is_locked = 0only when no instance is running.
Tip
For debugging monitor check failures, set
UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_IDto the ID of the failing monitor. This logs the full HTTP response body, which is otherwise truncated in heartbeat messages.