Server monitoring means a program keeps checking whether your server and the services on it work, and tells you when something is wrong. For most small setups two building blocks are enough. An uptime monitor such as Uptime Kuma checks from the outside whether your website answers. A metrics monitor such as Netdata or Prometheus looks from the inside at CPU, RAM, disk and services. What matters is not the dashboard but the alert: it has to reach a human who actually reads it.
That sounds simple, and installing it is. Each of the three big open-source tools came up on our server in under three seconds. The hard part is somewhere else: monitoring can show green while your site is broken. In our test an HTTP monitor reported “OK” for 70 seconds while the website served nothing but “Error: database connection failed”. And Prometheus raised an alert correctly, then sent it to nobody, without a single warning.
On 6 November 2026 we started Uptime Kuma 2.5.9, Netdata 2.12.3, Prometheus 3.16.0 with Node Exporter and Grafana 13.2.5 as throwaway containers on our own server, pointed them at a small test site and broke that site on purpose. This article shows what you should monitor, which tool suits you, how to set it up and where the silent gaps are. If your server is brand new, start with our guide on Linux server setup and come back here afterwards.
Video: Tech with Gerard sets up Uptime Kuma for a whole stack of self-hosted services and shows status pages and notifications (June 2026). Our measurements below were made independently.
What is server monitoring and why do you need it?
Without monitoring you learn about an outage from your users. Or not at all. A website that goes down at 3 a.m. may be gone for five hours before breakfast, and you only notice when a customer writes in. A server whose disk is slowly filling up runs fine for weeks and then stops on some random Tuesday because the database cannot write another row. Neither happens suddenly. Both announce themselves to anyone who looks.
Server monitoring is that looking, automated. It answers three questions:
- Is it reachable? Does the website, the mail server, the API answer? That is your users’ view.
- Is it healthy? How full are disk and memory, how high is the load, are all services running? That is the view from inside.
- Will it become a problem soon? Is the disk growing two percent every week? Does the TLS certificate expire in ten days? That is the view ahead.
Then there is the fourth part, the one most often forgotten: who finds out? A dashboard nobody opens is wallpaper. An alert that lands in a log file is a comment. Monitoring is only finished when a problem reaches you in a way you actually notice: a push on your phone, a message in your messenger, an email in an inbox you read.

What should you monitor on a server?
The list is shorter than most dashboards suggest. Without any configuration Netdata created 3,302 charts for us. You need maybe ten of them. For a typical web server these values matter most:
| What | Why | Typical threshold |
|---|---|---|
| HTTP response of your website | The only number users feel directly | Wrong status or content, or response over 10 s |
| Disk usage | Full disk = database stops, logs disappear | Warning at 80 %, critical at 90 % |
| Memory pressure (PSI) | Shows whether the server really lacks RAM | some avg60 above 10 for a sustained period |
| CPU load | Sustained high load slows everything | Load consistently above number of cores |
| Running services | A crashed service often produces no visible error | Service not active |
| TLS certificate | Expired certificate = browser warning | Less than 14 days left |
| Backups and cron jobs | A silent cron job looks exactly like a successful one | No sign of life for X hours |
Two items in this table matter more than they appear to.
Almost everyone measures memory wrong. The “free” value on a healthy Linux server is nearly always small, because the kernel uses spare memory as cache. An alert on it fires constantly without anything being broken. We measured this in detail in How much RAM does a server need? The reliable value is /proc/pressure/memory. It shows how much time processes actually spend waiting for memory. We saw exactly this misunderstanding live in the test: right after starting, Netdata flagged “swap 99.9 % used” as CRITICAL on our server, while the same server had 16 GB of memory available. A full swap is not a problem. High memory pressure is.
A silent cron job is the nastiest problem. Your backup script runs every night at 3 a.m. If it fails, you notice through an error email. But if the cron job stops starting at all, because someone overwrote the crontab, there is no error email. There is nothing, and “nothing” looks exactly like “all fine”. Only a reversed check helps: the script reports to your monitoring after every successful run, and the monitoring alerts you when that report does not arrive. Uptime Kuma calls this a “push” monitor, other services call it “heartbeat” or “dead man’s switch”. How to set up the cron job itself cleanly is covered in Create a cron job.
The tools: Uptime Kuma, Netdata and Prometheus with Grafana
There are dozens of monitoring tools. For beginners and small to medium setups three open-source projects have won out, and they answer very different questions.
Uptime Kuma is an uptime monitor with a pleasant web interface. You create monitors (HTTP, keyword, ping, TCP port, DNS, Docker container, push and several more) and Kuma checks them on a fixed schedule. When something fails it notifies you through one of more than 90 channels, including email, Telegram, Discord, Signal, Slack and ntfy. Uptime Kuma does not look inside your server. It only sees what a visitor sees.
Netdata is the opposite: an agent that runs on the server and measures everything it can find. CPU per core, every disk, every network interface, every Docker container, databases, web servers. It ships with a large number of preconfigured alerts and a dashboard that works straight away. In recent versions the local dashboard is partly tied to Netdata Cloud, but the agent itself remains free to use.
Prometheus with Grafana is the construction kit. Prometheus collects metrics from so-called exporters (for the server itself that is Node Exporter), stores them as time series and evaluates alert rules. Grafana draws dashboards from that data. Alertmanager sends the alerts. This is more powerful and flexible than anything else here, but you assemble it from four separate parts, and each of them wants configuring.
Alongside these there are lightweights such as Beszel (very small agent, simple dashboard), classic systems such as Zabbix and Checkmk, and hosted services such as UptimeRobot or Better Stack. We did not measure those in this test.
Our test setup
All tools ran as Docker containers on our development server (12 vCPU AMD EPYC, 22 GB RAM, Ubuntu 24.04). We bound the ports to 127.0.0.1 only, so nobody could reach them from outside. The test target was a tiny Node.js web server we could switch between four states with a command:
- ok: HTTP 200 with “Shop OK”
- dberr: HTTP 200, but the page only shows “Error: database connection failed”, like a shop whose database is gone (the actual test string was German)
- down: connection is dropped immediately, like a crashed service
- slow: response only after 40 seconds, like an overloaded server
In Uptime Kuma we pointed two monitors at the same page: a plain HTTP monitor and a keyword monitor that additionally looks for “Shop OK”. Check interval 20 seconds, timeout 16 seconds, no retries. In Prometheus an alert rule “site unreachable for 1 minute” ran with a 15-second scrape interval. At the end we deliberately deleted all containers, the network, the images and the data volume again.
Resources compared: what the tools cost
| Uptime Kuma 2.5.9 | Netdata 2.12.3 | Prometheus 3.16 | Node Exporter | Grafana 13.2 | |
|---|---|---|---|---|---|
| Image size | 2.53 GB | 1.33 GB | 389 MB | 41 MB | 1.38 GB |
| Start to HTTP response | 2.6 s (+ database choice) | 1.2 s | 0.4 s | – | 2.7 s |
| RAM at idle | 114–144 MiB | 151–173 MiB | 31–32 MiB | 8–9 MiB | 288–333 MiB |
| CPU at idle | ~0 % | 12–13 % of one core | 0.3 % | ~0 % | 0.6–0.9 % |
| Data after ~15 min | 1.5 MB | 18 MB | 0.5 MB | – | – |
A few things stand out.
Netdata is the only one that constantly uses CPU. 12 to 13 percent of one core, measured over five samples. That is because Netdata measures every second by default and runs through 38 collectors each time. On our 12-core machine that is one percent of total capacity. On a small single-core VPS it is 12 percent, just for watching. You can reduce it through the collection interval, but you should know about it.
Prometheus plus Node Exporter together stay under 45 MiB. The memory appetite of the classic stack sits in Grafana, not in Prometheus. If you mainly want alerts and rarely need dashboards, you can drop Grafana or run it on another machine.
Uptime Kuma is frugal in operation, but the image is huge. 2.53 GB, because it bundles a headless browser for checking JavaScript pages and an embedded MariaDB. There is a “slim” variant without these extras. On a server with little disk that makes a difference.

The trap: green while the website is broken
This is the most important section of this article. We switched the test page through the four states and logged what Uptime Kuma reported.
| Page state | HTTP monitor | Keyword monitor |
|---|---|---|
| ok | UP (2–5 ms) | UP |
| dberr: HTTP 200 + error text | UP, 4 of 4 checks over 70 s | DOWN on the first check, 10 s after the switch |
| down: connection dropped | DOWN “socket hang up” on the next check | DOWN |
| slow: 40 s response | DOWN “timeout of 16000ms exceeded” | DOWN |
| ok again | UP on the next check | UP |
The HTTP monitor did not notice the broken database. From its point of view all was well: the server answered, the status code was 200, the response arrived in three milliseconds. A plain HTTP monitor does not check that the page only contained an error message.
This is not a contrived case. Many PHP applications, CMSs and shops deliver an error page with status 200 when the database fails. The same goes for a maintenance page, a blank white page after a failed deployment, or a web server’s default page after someone overwrote the configuration. For your users all of that is an outage. For an HTTP monitor it is a success.
The fix is simple: don’t just check that the page answers, check that the right content is there. In Uptime Kuma that is “HTTP(s) - Keyword”. Pick a word that only appears on the working page and depends on the database, such as a product name or footer text that comes from the CMS. Even better is a dedicated health endpoint that checks database, cache and storage internally and only returns “OK” when everything is fine. Uptime Kuma also has “HTTP(s) - Json Query” for checking a value in a JSON response.

A second detail from the table: in the slow state the check spacing grew from 20 to 36 seconds, because Kuma first waits out the 16-second timeout and only then starts the interval. With a 60-second interval and a generous 48-second timeout, Kuma checks a hanging page only about every two minutes. So set the timeout well below the interval. For normal websites 10 to 15 seconds is enough.
And the third point: we deliberately set no retries so we could see every single check. In real operation you should set one or two retries. Otherwise every network hiccup wakes you up. With a 60-second interval and two retries you hear about a real outage after roughly two to three minutes. For most websites that is a good compromise.
Video: Snehasis Ghosh walks through a self-hosted Uptime Kuma setup with Docker, including monitor types and notifications (July 2026).
The second trap: an alert that reaches nobody
With Prometheus we stopped the test site and queried the state of the alert rule every second:
- +23 seconds: alert “pending”. Prometheus noticed the site is not responding, but waits out the configured minute.
- +83 seconds: alert “firing”. The condition has been true for a minute, the alert is active.
Up to this point everything works. Then we checked where Prometheus had sent the alert: activeAlertmanagers: []. Nowhere. Prometheus itself does not send notifications. A separate program, Alertmanager, does that. If none is configured, Prometheus dutifully evaluates every rule, shows the alert in red in its own web interface and sends nothing. There is no warning in the log and no hint at startup.
That is exactly how setups are born that “run” for months: someone installs Prometheus and Grafana, builds nice dashboards, writes alert rules, sees in the UI that they fire, and considers the job done. Until the first real outage.
With Netdata it is the other way round, but not necessarily better. Netdata ships 1,179 preconfigured alert rules and by default sends them by email to the local user root, via the sendmail inside the container. On a typical VPS without configured mail delivery that ends up somewhere or nowhere. And even if the mail arrives: after one minute we already had two active alerts, one of them the false swap alert from above. Anyone who gets that every day eventually stops reading. That is the most dangerous kind of monitoring: one that cries wolf.
Two rules follow from this:
- Test the alert path, not the alert rule. After setting things up, deliberately break something (stop a service, set a wrong keyword) and wait until something arrives on your phone. Only then is your monitoring done. Uptime Kuma has a “Test” button in every notification. It only checks the channel, not your monitor. Do both.
- Better a few alerts that are right than many that annoy. Switch off alerts that regularly fire for no real reason on your system, or adjust the threshold. Every false alarm makes the next real one quieter.
The third trap: monitoring on the same server
Most tutorials run Uptime Kuma, Netdata and Prometheus on exactly the server they monitor. For metrics from inside that is unavoidable and correct. For the question “is my server reachable?” it is useless. If the server goes down, the monitoring goes down with it. It cannot report its own death.
The same holds on a smaller scale. If Uptime Kuma runs on the web server itself, it checks the website via the local network interface. A broken firewall rule, an expired DNS record, a network problem at your host: a local monitor sees none of that, because it never takes the detour through the internet.
Our practice: the uptime monitor runs on a second, small server in a different data center. It costs a few euros a month and checks the main server from outside. It only speaks up when something really fails there. If you don’t have a second server, a hosted service with a free tier works for the outside view. What matters is that the check comes from somewhere that does not fail together with your server.

Setting up Uptime Kuma step by step
For most readers Uptime Kuma is the best starting point. It is set up in ten minutes and covers the most important question: is my website fine for visitors? The easiest way to install it is Docker Compose. What Docker is and how Compose works is explained in What is Docker? and, with ready-made examples, in Docker Compose example.
# docker-compose.yml
services:
uptime-kuma:
image: louislam/uptime-kuma:2
container_name: uptime-kuma
restart: unless-stopped
ports:
- "127.0.0.1:3001:3001"
volumes:
- ./kuma-data:/app/data
Two lines in there matter more than the rest.
127.0.0.1:3001:3001 binds the interface to the local machine only. You make it reachable through a reverse proxy with HTTPS (see What is a reverse proxy?) or via an SSH tunnel. If you write just 3001:3001, the interface is open to the whole internet, and Docker even bypasses the UFW firewall to do it.
./kuma-data:/app/data puts the database in a folder next to the Compose file. Without this line Docker creates an anonymous volume. That is exactly what happened to us: after deleting the container, a nameless volume with all monitors was left on disk, easy to overlook and easy to delete in the next cleanup.
Then start it:
docker compose up -d
docker compose logs -f uptime-kuma
On first visit Uptime Kuma 2 first asks which database to use: the built-in SQLite, an embedded MariaDB or an third-party MariaDB/MySQL. For a single server with up to a few dozen monitors SQLite is perfectly sufficient. After that you create the admin account.
Important: the admin account is created by whoever opens the page first. There is no one-time code and no password from a file. In our test we created the account by script through the websocket interface, without any authentication. So only expose the interface publicly after you have created your account.
Your first monitors
- Main page as a keyword monitor. Type “HTTP(s) - Keyword”, URL of your homepage, keyword a text that only appears on the working page. Interval 60 s, retries 2, timeout 15 s.
- An important subpage (checkout, login, API endpoint), also as a keyword or JSON monitor. Homepages are often cached and keep working when everything behind them is already broken.
- Certificate expiry. In the advanced settings of any HTTPS monitor you can enable “Certificate Expiry Notification”. Kuma then warns you before expiry. How automatic renewal works and why it can fail silently is covered in Create an SSL certificate.
- Push monitor for backups. Type “Push”, Kuma shows you a URL. Append it to the end of your backup script, but only call it if the backup succeeded:
#!/bin/bash
set -euo pipefail
/usr/local/bin/backup.sh
curl -fsS -m 10 "https://status.example.com/api/push/AbCdEf123?status=up&msg=OK" > /dev/null
Set the push monitor’s interval a little longer than the gap between two backups, for a daily backup 25 hours for example. If the report doesn’t arrive, because the script failed or never started, Kuma alerts you. Thanks to set -e the script never reaches the curl command after an error. Why that matters so much for backups is shown by our measurements in the article on the 3-2-1 backup strategy.
- Set up a notification. Under “Settings → Notifications” create a channel and tick “Default enabled” so every new monitor gets it. Press the test button, then, as described above, simulate a real outage.
Setting up Netdata: metrics from inside
If you want to know why your server is slow, you need metrics from inside. Netdata is the fastest way to get them. The official installation is a script directly on the system or a Docker container. With Docker, Netdata needs read access to the host’s /proc and /sys, otherwise it only sees itself:
docker run -d --name netdata \
--pid=host --cap-add SYS_PTRACE \
-v /proc:/host/proc:ro -v /sys:/host/sys:ro \
-v netdataconfig:/etc/netdata -v netdatacache:/var/cache/netdata \
-p 127.0.0.1:19999:19999 \
--restart unless-stopped \
netdata/netdata:stable
The same rule applies: put 127.0.0.1: in front of the port. Inside the container Netdata listens on all interfaces, and in our test the API was reachable without any authentication. A single call to /api/v1/allmetrics returned 17,302 lines with every metric of the server: process names, mount points, network interfaces, container names. For an attacker that is a very convenient map. If you want to see the dashboard from outside, put a reverse proxy with a password in front of it or use a VPN.
After startup it is worth looking at the active alerts. Adjust thresholds that don’t make sense for you (Netdata calls this configuration “health”, files under /etc/netdata/health.d/, edited via edit-config). And set up a real notification path instead of relying on sendmail: Netdata supports ntfy, Telegram, Discord, Slack and webhooks, among others.
For a small single-core VPS you should also raise the collection interval from one to five or ten seconds. That cuts CPU usage noticeably, and a web server does not need per-second graphs.
Prometheus and Grafana: when the construction kit pays off
The Prometheus stack pays off when you monitor several servers, run your own applications with their own metrics or want to analyse data over months. For a single blog server it is too much work.
The minimal setup has four parts: Node Exporter on each server, Prometheus with a prometheus.yml that scrapes the exporters, Alertmanager for delivery and optionally Grafana. An alert rule looks like this:
# rules.yml
groups:
- name: basics
rules:
- alert: WebsiteDown
expr: up{job="web"} == 0
for: 1m
- alert: DiskAlmostFull
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.1
for: 10m
And in prometheus.yml you must add Alertmanager, otherwise you get what we measured above:
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
You can check this at any time with curl -s localhost:9090/api/v1/alertmanagers. If the list is empty, no alert will reach you.
Two more values from the test: Prometheus keeps data for 15 days by default (storage.tsdb.retention.time). For long-term comparisons you need to raise that and plan for disk space. And like Netdata, the Prometheus API was reachable without authentication. So bind it locally as well. Grafana accepted the default login admin:admin via its API in our test. The UI does ask for a new password on first login, but only once someone actually logs in there. So set the admin password via environment variable (GF_SECURITY_ADMIN_PASSWORD) before the container starts for the first time.
Which server monitoring suits you?
| Situation | Recommendation |
|---|---|
| One server, one website, you want to know about outages | Uptime Kuma on a second machine, or a hosted uptime service |
| One server, and you also want to understand why it is slow | Uptime Kuma (third-party) + Netdata (on the server) |
| Several servers, your own applications, analysis over months | Prometheus + Node Exporter + Alertmanager + Grafana, plus an third-party uptime check |
| Very small VPS where every MB counts | Uptime Kuma third-partyly + Node Exporter (9 MiB) or Beszel |
| Homelab with many services | Uptime Kuma with Docker container monitors + status page |
If you only do one thing, do this: a keyword monitor on your most important page, from a different machine, with a notification you have tested. That covers most real outages, costs nothing and takes fifteen minutes. Build everything else on top once you notice information is missing.
Frequently asked questions
What is the difference between uptime monitoring and server monitoring?
Uptime monitoring checks from the outside whether a service answers, like a visitor would. Server monitoring in the narrower sense measures values such as CPU, RAM, disk and services from inside. You need both: uptime monitoring tells you that something is broken, server monitoring tells you why.
Is Uptime Kuma free?
Yes. Uptime Kuma is open source under the MIT licence and costs nothing. You only need a machine to run it on. Ideally that is not the same server it monitors.
How much RAM does Uptime Kuma need?
In our test Uptime Kuma 2.5.9 used 114 MiB at idle and 144 MiB with two active monitors. A VPS with 512 MB RAM is enough for Kuma on its own. The Docker image, however, is large at 2.53 GB. The “slim” variant is considerably smaller.
Netdata or Prometheus: which is better for beginners?
Netdata. It works after one command, detects services automatically and comes with ready-made alerts. Prometheus is more flexible, but you have to assemble exporters, rules, Alertmanager and dashboards yourself. In return Prometheus with Node Exporter needs only about 40 MiB of RAM and hardly any CPU, while Netdata used a constant 12 to 13 percent of one core on our machine.
How often should a monitor check?
For normal websites a 60-second interval with two retries is a good value. You hear about a real outage after roughly two to three minutes without every short network glitch waking you. Shorter intervals only pay off when every minute of downtime really costs money.
Why does my monitoring show green although the site is broken?
Usually because the monitor only checks the HTTP status code. Many applications deliver error pages with status 200. In our test an HTTP monitor stayed green for 70 seconds on a database error page, while a keyword monitor fired after 10 seconds. So always check for content that only appears on the working page.
Can I monitor my server with a tool on the same server?
For metrics such as CPU and disk, yes. For reachability, no: if the server goes down, the monitoring goes down too and can no longer tell you anything. The uptime check should come from a second machine or an third-party service.
How do I get notified about an outage?
Uptime Kuma ships more than 90 channels, including email, Telegram, Discord, Slack, Signal (via an add-on service), ntfy and webhooks. Netdata supports email, Telegram, Discord, Slack, ntfy and webhooks, among others. Prometheus only notifies through the separate Alertmanager. What matters is testing the whole path once with a real outage. A test button only checks the channel, not whether your monitor fires at all.
How do I monitor cron jobs and backups?
With a reversed check, often called a “heartbeat” or “push monitor”. The script calls a URL after every successful run. If the call doesn’t arrive within the expected time, the monitoring raises an alert. That way you also catch jobs that no longer start at all. Those would otherwise never produce an error.
Which memory values should I monitor?
Not “free”, but memory pressure from /proc/pressure/memory, or at least “available”. Free memory on a healthy Linux server is nearly always low because the kernel uses it as cache. Netdata flagged a full swap as critical on our server while 16 GB of memory was available.
Is it dangerous to make monitoring dashboards public?
Yes. In our test Netdata and Prometheus were reachable without authentication and expose processes, mount points, containers and network details. Grafana accepted the default password via its API. Bind all monitoring ports to 127.0.0.1 and only make them accessible through a reverse proxy with authentication or via VPN.
Conclusion: monitoring is done when the alert arrives
Installation is the easy part with every tool we tested. Uptime Kuma, Netdata and Prometheus start in seconds, and the dashboards look good immediately. What makes monitoring valuable comes down to three unspectacular decisions.
Check the content, not just the status code. An HTTP monitor that takes a database error page for “OK” gives you false confidence. A keyword monitor costs no extra minute and found the error in ten seconds for us.
Check from the outside. A monitor that fails together with the server never reports exactly the worst case. A second small machine or an third-party service solves that.
Check the alert path. Prometheus raised an alert correctly for us and sent it to nobody. Netdata raised an alert that wasn’t one. Both only show up when you rehearse the real thing. So: break something on purpose once and wait until your phone rings. Only then can you sleep soundly.
