Uptime Kuma
Uptime checks, alerting and status pages in one container, no separate database
Uptime Kuma polls HTTP endpoints, TCP ports, DNS records, containers, MQTT topics and push heartbeats on a schedule, then notifies through Apprise and around 90 built-in providers. It covers what people rent UptimeRobot or Pingdom for, plus public status pages, on hardware you already own.
Our verdict on Uptime Kuma#
The default answer for "is my stuff up" if you want a UI rather than a config file. Version 2 is out of beta and current: 2.5.3 shipped in August 2026, and 2.x is the tag the project publishes as louislam/uptime-kuma:2. The big v2 change is storage. v1 wrote one heartbeat row per check forever, so 50 monitors at 60 seconds meant roughly 72,000 rows a day and multi GB SQLite files; v2 keeps 24 hours of raw beats and rolls the rest into minute, hour and day tables, with a 365 day default retention. MariaDB is supported in v2, but the project explicitly does not support migrating an existing SQLite database into it, so treat that as a new install decision. The honest limit: one instance is one vantage point, and it cannot tell you it went down itself. Pair it with an external ping or a second instance elsewhere.
Who Uptime Kuma is for#
Choose it if
- Someone running 10 to 100 self-hosted services who wants alerts in Telegram or ntfy without writing YAML
- A small business that needs a public status page mapped to its own domain
- Anyone replacing a paid uptime checker whose free tier caps intervals at five minutes
- Homelabbers who want certificate expiry and domain expiry warnings alongside uptime
Look elsewhere if
- You want your monitoring defined in git and reviewed like code; Gatus fits that shape and Uptime Kuma does not
- You need metrics, not up or down: it does not store CPU, memory or disk series
- You need highly available checks from multiple regions, which the project has no clustering story for
What running it actually looks like#
One container, one volume at /app/data, port 3001, and nothing else to stand up. First run creates the admin account in the browser; there is no seed password to rotate. The README warns that NFS is not supported for the data volume, and it means it: SQLite locking over NFS or CIFS corrupts the database. A rootless image tag exists if you do not want the default root entrypoint. The image is large for what it does (around 600 MB compressed) because it bundles Apprise, cloudflared and nscd. The upgrade that bites is v1 to v2: on first start it aggregates the heartbeat table, which the migration guide says took about 7 minutes for 20 monitors and 90 days of data, and can run for hours on slower hardware. Interrupt it and you restore from backup.
Resource profile#
- Memory
- 120 MB idle, around 250 MB in ordinary use. A single Node process. Heap tracks the number of monitors and open dashboard sockets, not the length of history.
- CPU and acceleration
- Flat between checks, with spikes when many monitors land on the same second. The one real CPU event is the v1 to v2 heartbeat aggregation, which runs once and can hold a core for minutes or hours.
- Storage growth
- v2 keeps only 24 hours of raw heartbeats per monitor plus minute, hour and day aggregates, so a 50 monitor instance settles in the low hundreds of MB; v1 databases that were never pruned commonly arrive at several GB.
- Operational load
- 2 of 5, Light. One container, one SQLite file, upgrades inside 2.x are pull and restart. It is a 2 rather than a 1 because the v1 to v2 upgrade is a one way data migration you must not interrupt, and because the database needs an occasional manual shrink.
Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.
Data and backup surface#
Everything lives in /app/data: kuma.db (SQLite in WAL mode), plus uploads and screenshots. A live cp of the file alone is not safe because the WAL may hold recent commits; either stop the container and copy the whole directory, or run sqlite3 kuma.db ".backup out.db" against a copy. Nothing in there is regenerable, including notification credentials and status page config. If you moved to MariaDB, back it up with mysqldump and keep /app/data as well, since uploads still live on disk.
Derived backup shape
SQLite backup. sqlite3 <db> ".backup out.db" while running, or stop the container and copy. A live cp of a WAL-mode SQLite file can restore corrupt.
Traps and surprises#
The v1 to v2 migration is one way
Upgrading rewrites heartbeat history into aggregate tables. There is no downgrade path, and the wiki is explicit that an interrupted migration means restoring from backup and starting again. Snapshot /app/data first, pin the old image tag so you can roll back the container, and give the migration an uninterrupted window rather than starting it before you leave the house.
Deleting old data does not free disk
Retention defaults to 365 days and the cleanup job deletes rows, but SQLite returns those pages to its own free list rather than to the filesystem. The database file keeps its size until you use Shrink Database in Settings, which runs VACUUM. Expect the shrink to need free space roughly equal to the current database size while it runs.
SQLite on network storage corrupts
The README explicitly lists NFS as unsupported for the data volume, and CIFS behaves the same way: SQLite's locking assumptions do not survive it. Symptoms are 'database is locked' errors and, eventually, a database that will not open. Keep /app/data on local disk or a local Docker volume, and back up to the NAS instead of running from it.
Reverse proxies must pass WebSockets
The dashboard is socket.io. A proxy configured for plain HTTP gives you a page that loads, then spins forever with no monitors and no error. In nginx that means proxy_set_header Upgrade $http_upgrade and Connection "upgrade"; in Caddy and Traefik it works by default. The same applies to status pages behind Cloudflare with WebSockets disabled.
MariaDB is not a migration target
v2 can run on MariaDB, but the project does not support converting an existing SQLite install and warns that third party converters miss indexes, which wrecks query performance. If you want MariaDB, decide at install time. Most single-operator instances with fewer than a few hundred monitors have no reason to leave SQLite.
Specifications#
- Category
- Monitoring and observability
- Licence
- MIT (Permissive)
- Written in
- JavaScript / Node.js
- First release
- 2021
- Maturity
- Mature
- Datastore
- SQLite by default, MariaDB optional in v2
- Default ports
3001- Ships as
- docker, node.js + pm2
- arm64 builds
- Yes
- Identity
- Local accounts only. Local accounts with optional TOTP only; the OAuth support in the codebase authenticates monitored endpoints, not people logging in.
- Replaces
- UptimeRobot, Pingdom, StatusCake, Better Stack Uptime
- Project site
- uptime.kuma.pet
- Source
- github.com/louislam/uptime-kuma
- Documentation
- github.com/louislam/uptime-kuma/wiki
Alternatives to Uptime Kuma#
Everything else in monitoring and observability, closest in operational weight first.
| Tool | Ops load | Idle RAM | Licence |
|---|---|---|---|
| Grafana | 2, Light | 150 MB | AGPL-3.0-only |
| Netdata | 2, Light | 150 MB | GPL-3.0-or-later (agent); dashboard UI under NCUL1 |
| Beszel | 1, Set and forget | 40 MB | MIT |
| Dozzle | 1, Set and forget | 15 MB | MIT |
| Gatus | 1, Set and forget | 25 MB | Apache-2.0 |
| Prometheus | 3, Moderate | 150 MB | Apache-2.0 |
Where Uptime Kuma comes up elsewhere#
Head to head
Migration guides
Guides
Reference
Sources#
Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.