Netdata Basics - Real-Time System Monitoring
Status: Active
Last Updated: 2026-08-14
Category: Observability - Phase 1: Basic Monitoring
Prerequisites: linux-fundamentals
Time: 1-2 hours
Tags: netdata, metrics, real-time, system-monitoring, docker, alerts, zero-config
Summary
Netdata gives you per-second visibility into everything a Linux box is doing โ CPU, memory, disks, network, containers, and every application it can auto-detect โ with literally zero configuration and ~150 MB of RAM. This guide installs Netdata two ways (kickstart script and Docker), tours the dashboard, configures sensible alert notifications, and shows how to stream metrics between nodes.
๐ฏ What You'll Learn
By the end of this article, you'll be able to:
- โ Install Netdata on bare metal and in Docker
- โ Read the dashboard: CPU, memory, disk I/O, and network charts
- โ Understand what "zero-config" actually detects automatically
- โ Monitor Docker containers from inside Netdata
- โ Configure alert notifications (email/Discord/Slack) without spamming yourself
- โ Decide when Netdata alone is enough vs when you need Prometheus (Phase 2)
Table of Contents
- Why Netdata
- Installation
- The Dashboard Tour
- What Zero-Config Detects
- Container Monitoring
- Alert Configuration
- Streaming Between Nodes
- Netdata or Prometheus?
- Troubleshooting
- Key Takeaways
- Next Steps
Why Netdata
Every monitoring tool answers: how much detail, how fast, at what setup cost?
| Tool | Resolution | Setup cost | RAM |
|---|---|---|---|
top/htop |
1โ3 s | Zero | Zero โ but only while you watch |
| Uptime Kuma | Per check | Minutes | ~100 MB |
| Netdata | Per second | ~10 minutes | ~100โ200 MB |
| Prometheus stack | 15โ60 s | Hours | 1โ4 GB+ |
Netdata's niche is the top-right corner: per-second resolution with near-zero setup. It's the tool you install first on any new machine because within five minutes you can answer "what is this server actually doing right now?"
Its superpower is context during incidents: when an alert fires elsewhere, Netdata's dashboard shows you the exact second things went sideways and every metric that moved with it.
Installation
Option A: Kickstart Script (bare metal / VM)
The official one-liner installs a static build that auto-updates:
wget -O /tmp/kickstart.sh https://get.netdata.cloud/kickstart.sh
sh /tmp/kickstart.sh --stable-channel
What happens:
1. Detects your distro and architecture
2. Downloads the static Netdata binary bundle
3. Installs to /opt/netdata
4. Creates systemd service "netdata"
5. Starts Netdata listening on port 19999
6. Enables auto-updates via netdata-updater.timer
Verify:
sudo systemctl status netdata # active (running)
curl -I http://localhost:19999 # HTTP 200
Open http://<server-ip>:19999 โ the dashboard appears instantly, already full of charts.
๐ Bind Netdata to localhost or keep it behind the firewall/VPN; the dashboard exposes detailed system internals. For remote access use an SSH tunnel (
ssh -L 19999:localhost:19999 user@host) or reverse proxy with auth (kb/security/ssh-security-hardening).
Option B: Docker
services:
netdata:
image: netdata/netdata:stable
container_name: netdata
restart: unless-stopped
pid: host # see all processes, not just container's
cap_add:
- SYS_PTRACE # process-level metrics
- SYS_ADMIN # cgroup/network details (optional)
security_opt:
- apparmor:unconfined
ports:
- "127.0.0.1:19999:19999" # localhost only!
volumes:
- ./config:/etc/netdata
- ./lib:/var/lib/netdata
- ./cache:/var/cache/netdata
- /:/host/root:ro,rslave
- /etc/passwd:/host/etc/passwd:ro
- /etc/group:/host/etc/group:ro
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- DOCKER_HOST=unix:///var/run/docker.sock
This is the standard privileged-monitoring pattern: mount /proc, /sys, and the host root read-only under /host so Netdata sees the host's reality rather than the container's isolated view.
docker compose up -d
docker logs netdata --tail 20 # look for "ready" without error lines
First Configuration (both options)
Edit health_alarm_notify.conf early so test alerts have somewhere to go:
# bare metal
sudo nano /opt/netdata/etc/netdata/health_alarm_notify.conf
# docker
nano ~/apps/netdata/config/health_alarm_notify.conf
Set at minimum:
DEFAULT_RECIPIENT_EMAIL="ops@yourdomain.com"
role_recipients_email[sysadmin]="ops@yourdomain.com"
Then restart and force one notification round so sender credentials get built:
sudo systemctl restart netdata
sudo su -s /bin/bash netdata
/opt/netdata/usr/libexec/netdata/plugins.d/alarm-notify.sh test
You should receive three test notifications (raise/clear/warning). If nothing arrives, fix delivery now, before you rely on it at 2 AM.
The Dashboard Tour
Open the dashboard. It's overwhelming by design โ hundreds of charts, all updating every second. Here's the reading order that matters:
1. CPU Section
- system.cpu โ total utilization, stacked per state:
user,system,iowait,idle - Watch iowait specifically: high iowait with modest CPU% means disks are the bottleneck, not processors
- softirq spiking usually indicates heavy network interrupt load
2. Memory Section
- mem.available โ the number that matters (not "free"!). Linux deliberately uses spare RAM for cache; available accounts for that
- mem.swap โ any sustained swap-in/out activity is a red flag
- If OOM risk exists, you'll see available trending toward zero here days before disaster โ this chart is your slow-leak detector from why-monitor
3. Disk Section
- disk.io โ read/write throughput per disk
- disk.util โ % time the device was busy; sustained >80% = saturated disk
- disk.await โ average wait per I/O op. Healthy SSDs: <1 ms. If this climbs into double digits, storage is choking
4. Network Section
- net.
โ throughput per interface - net_packets โ packet rate; huge packet counts with small bandwidth = many tiny requests (often a misbehaving client)
- ipv4.tcpsock / retransmits โ retransmit spikes indicate network quality problems
5. Apps & Containers Sections
Per-process-group and per-container breakdowns of CPU/RAM/disk โ this answers "which container ate all the memory?" without running anything.
Reading Charts Like a Pro
Anomaly checklist for any suspicious graph:
1. When exactly did it change? โ hover for timestamp
2. What else changed at that second? โ scan other sections at same time
3. Is there a daily/weekly pattern? โ normal cycle vs true anomaly
4. Did it recover? โ incident vs ongoing bleed
Correlating across sections at the same timestamp is Netdata's killer debugging move: disk.util spikes + iowait spikes + postgres writes = your backup job, probably.
What Zero-Config Detects
Out of the box, with no configuration files touched, Netdata auto-detects and starts collecting from:
| Category | Examples |
|---|---|
| System | CPU, memory, swap, disks, filesystems, network, kernel |
| Containers | All Docker/containerd/Podman cgroups |
| Databases | PostgreSQL, MySQL/MariaDB, Redis, MongoDB |
| Web | Nginx, Apache, Traefik, Caddy |
| Services | systemd units, supervisord |
| Hardware | SMART disk health, sensors, UPS (nut) |
| Apps | Any app exposing StatsD or Prometheus endpoints |
Detection works via local ports and sockets โ if Postgres listens locally, Netdata finds it and starts charting connections, transactions, cache hits within a minute of first contact.
Check what yours found: scroll the dashboard sidebar, or:
sudo /opt/netdata/usr/libexec/netdata/plugins.d/go.d.plugin --list
If something you run isn't detected, its collector may need credentials (e.g., a Postgres monitoring user) โ each collector has a config in /etc/netdata/go.d/.
Container Monitoring
With the Docker socket mounted (or bare-metal install), Netdata shows every container as a first-class citizen:
- cgroups section: CPU, memory, throttle events, I/O per container
- Memory limits: charts show usage against the container's limit โ catching containers about to be OOM-killed inside their cgroup even when the host has RAM free
- Network per container: which container is generating that traffic spike
Practical checks to run once after install:
- Sort containers by memory โ does the biggest match expectations?
- Look for throttling on CPU-limited containers (they're slower than they should be)
- Find any container with growing RSS over hours โ future OOM candidate
This complements Uptime Kuma nicely: Kuma knows the container stopped; Netdata shows you the memory climb that caused it.
Alert Configuration
Netdata ships with hundreds of pre-written alarms โ disk space, RAM pressure, TCP retransmits, app-specific health โ tuned by the community. Your job is only to (a) route them and (b) silence the noise.
How Alarms Work
Each alarm definition (in .conf files under health.d/) follows this shape:
template: 10min_ram_usage
on: mem.available
calc: ($avail_now) * 100 / ($avail_total)
warn: $this <= 10
crit: $this <= 5
unit: %
Read: evaluate available memory every 10 s; warn below 10%, critical below 5%. You can override any value by copying the line into health.d/*.conf.edit and changing numbers โ stock definitions stay intact across upgrades.
Route Notifications
In health_alarm_notify.conf (from Installation):
# Email
EMAIL_SENDER="monitor@yourdomain.com"
DEFAULT_RECIPIENT_EMAIL="ops@yourdomain.com"
# Discord
SEND_DISCORD="YES"
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..."
DEFAULT_RECIPIENT_DISCORD="alerts"
Restart, then verify with the alarm-notify.sh test command shown earlier.
Fighting Alert Fatigue
Default alarms are chatty on small systems. Tame them:
- Silence noisy non-critical alarms globally:
# health_alarm_notify.conf
DEFAULT_RECIPIENT_EMAIL="ops@yourdomain.com|critical" # email only criticals
- Mute specific alarms by creating e.g.
health.d/cpu.conf:
# 10min_cpu_usage silenced: this box compiles things sometimes
template: 10min_cpu_usage
on: system.cpu
calc: $this
warn: $this > 999999
(Setting an impossible threshold = effective mute, while keeping the alarm visible.)
- Review monthly: like all alerts (why-monitor), any alarm that fired without requiring action gets demoted or muted.
Streaming Between Nodes
For 2โ10 servers, Netdata can stream metrics from child machines into a parent โ one dashboard, one place to check.
On each child, create stream.conf:
[stream]
enabled = yes
destination = parent-host:19999
api key = 11111111-2222-3333-4444-555555555555
On the parent, same file:
[11111111-2222-3333-4444-555555555555]
enabled = yes
default history = 3600
Restart both. Children appear in the parent's dashboard node list. Benefits: children need almost no RAM (metrics buffered briefly then forwarded), and if a child dies, the parent still shows its last-known data โ useful for post-mortems on machines that died completely.
Beyond ~10 nodes, graduate to Prometheus + Grafana (Phase 2โ3) โ see next section.
Netdata or Prometheus?
Both, at different stages:
| Question | Answer |
|---|---|
| One to five servers, want instant insight | Netdata alone โ done |
| Need year-long history and capacity trends | Netdata's DB is short-term; add Prometheus |
| Many targets, custom apps, team dashboards | Prometheus + Grafana (Phase 2โ3) |
| Want both? They coexist happily | Netdata exposes /api/v1/allmetrics?format=prometheus โ scrape it as one target |
That last row is the pragmatic path: keep Netdata for real-time forensics, let Prometheus pull its metrics for long-term storage, and Grafana graphs the history. Best of both with minimal duplication.
Troubleshooting
Dashboard loads but most charts are empty
- Docker install missing mounts (
/proc,/sys) โ charts for host resources stay blank. Recheck volume list. - Running inside an unprivileged LXC โ many collectors silently fail. Grant the needed caps or run outside the container OS.
High CPU from netdata itself
- Reduce chart retention: lower
dbengine multihost disk spaceor history innetdata.conf. - Disable collectors you don't use in
go.d.conf/python.d.conf.
Notifications not sending
- Run the manual test script as the
netdatauser (shown above); it prints the actual send error. - Gmail/SMTP: needs an app password, port 587 STARTTLS.
- Check
health_login the dashboard โ if alarms fire but recipients are empty, the notify conf wasn't loaded (restart required).
Can't see a specific container
- Cgroup naming differs between cgroup v1/v2 hosts โ update Netdata to current stable; v2 support is solid in recent releases.
Port 19999 occupied
Change [web] default port = 19998 in netdata.conf, restart, adjust any tunnel/proxy.
Key Takeaways
- Netdata = per-second, zero-config visibility โ install it on every box as step one.
- Read dashboards in order: CPU (incl. iowait) โ memory (available!) โ disk (util + await) โ network.
- Auto-detection covers databases, web servers, and containers out of the box.
- Container views include per-cgroup limits โ catch OOM candidates early.
- Hundreds of alarms ship enabled; routing + selective muting is your whole alert job.
- Stream to a parent for multi-node dashboards; scrape into Prometheus when you need long history.
- Test notification delivery immediately โ untested alerts don't exist.
๐ Related
- Next lesson: simple-alerts โ design a notification strategy that doesn't train you to ignore pagers
- Also in Phase 1: why-monitor, uptime-kuma-setup
- Course overview: README
- Related KB sections:
- kb/sysadmin/system-admin-basics โ what the charts are showing underneath
- kb/prometheus-introduction โ Phase 2 graduation path
- kb/containers/docker-volumes โ the volume-mount pattern used in the Docker install