Uptime Kuma vs Gatus
Pick Uptime Kuma — one container, a web UI where you click Add Monitor, and check history that survives a restart with nothing to configure. Pick Gatus when you want the monitor list to live in your repo and ship through the same review and deploy path as the services it watches — and set a storage type before you trust the numbers, because its default keeps history in memory and drops it on every restart.
Side by side
Both of these watch endpoints and shout when one stops answering, both are light enough to ride along on a server you are already paying for, and both cover much the same protocols. The difference is not what they can check — it is where the list of checks lives. Uptime Kuma keeps it in a database behind a web UI you click. Gatus keeps it in a YAML file you commit. That one decision changes who can add a monitor, how a monitor gets reviewed, and — by default — whether your uptime history is still there tomorrow.
Clicking versus committing
Uptime Kuma is one Node container. You open it, click Add Monitor, fill in a URL, an interval and a notification target, and you are monitoring. Nothing else to write, nothing to deploy. It earns the easiest difficulty rating in this catalogue (1 out of 5), and its appeal is that a non-technical teammate can add a check without touching a repo.
Gatus is a single Go binary and its dashboard is read-only. Every
endpoint is declared in config.yaml:
- a URL, an interval, and a list of conditions the response must satisfy
- conditions are expressions — status code, response time, a value inside a JSON body, a pattern in the page, days left on the TLS certificate
- point
GATUS_CONFIG_PATHat a directory and it merges every YAML file inside
There is no form. Adding a monitor is an edit, a review and a deploy, exactly like the services it watches. That is a feature if your team already works that way and friction if it does not.
The persistence trap
This is the part that catches people, and it is worth being blunt about: Gatus stores results in memory unless you tell it otherwise. Restart the container — an upgrade, a reboot, a host that OOMs — and uptime percentages, response-time history and past events are gone. The dashboard comes back looking healthy because it has nothing to remember.
The fix is one block of config: set the storage type to sqlite or postgres, give it a path or a DSN, and mount it. Do that on day one, before anyone quotes a number from the dashboard. Uptime Kuma has no equivalent footgun — it writes to its own embedded storage from the first check, and a mounted data volume is the whole backup story.
What each one checks
Coverage is close enough that it rarely decides anything:
- Uptime Kuma — HTTP(s), TCP port, ping, DNS, keyword and JSON-query matching, certificate expiry, plus push monitors for cron jobs that check in. Notifications reach an unusually long list of services.
- Gatus — HTTP, ICMP, TCP and DNS, each scored against its condition list, with alerting to Slack, Teams, Discord, PagerDuty, Twilio and others.
Both do status pages. Uptime Kuma's are built and styled in the UI; Gatus's are generated from the same config file as the checks.
Footprint
Gatus is the smaller of the two — the catalogue lists it at 256 MB against Uptime Kuma's 512 MB — because a Go binary reading a config file has less to carry than a Node runtime with an embedded database. Neither figure is published upstream; both are our estimates from running them. In practice either one fits on the cheapest instance any provider sells, and the RAM gap will not decide this for you. The config model will.
Which should you self-host?
Pick Uptime Kuma if…
- You want to be monitoring in the time it takes to start one container, with no file to write first.
- More than one person adds monitors, and not all of them work in the repo.
- You want history and uptime percentages to be correct without having thought about storage at all.
Pick Gatus if…
- You want monitoring reviewed and deployed like code — the check list in git, next to the service it watches.
- You like condition expressions: assert on a JSON field or a response-time budget, not just "did it return 200".
- You are happy to configure persistence explicitly, and you would rather have a single static binary than a Node app.
Running either on a VPS
Both are small enough to share a box with the services they monitor — though the usual caveat applies: a monitor on the same host cannot tell you the host went down. If uptime numbers matter, put whichever you choose on a different machine, ideally a different provider. Terminate TLS in front of either one, and if you go with Gatus, configure sqlite or postgres storage before you rely on a single figure it shows you. Setup steps and VPS options are linked below.