All articles
TutorialsJul 03, 2026 · 20 min read · By The RDP.sh Team

Self-Host Netdata on a VPS for Real-Time Server Monitoring

Self-Host Netdata on a VPS for Real-Time Server Monitoring

Most monitoring tools sample every 10 or 30 seconds. That's fine for a graph you check once a day, but useless when you're staring at a server that spiked, thrashed, and recovered inside a five-second window. By the time the next scrape lands, the evidence is gone.

Netdata collects everything per second and renders it live in the browser with essentially zero configuration. Install it and within a minute you have hundreds of charts - CPU per core, disk I/O per device, every network interface, memory, running services, Docker containers, and any database or web server it detects - all updating in real time. It's the closest thing to htop for your whole machine, with history and alerts bolted on.

The catch is that Netdata's dashboard listens on 0.0.0.0:19999 out of the box. That's a public, unauthenticated window into your server, and it needs to be closed before you do anything else. This guide installs Netdata, locks the dashboard down properly, tunes it so it doesn't eat a small VPS alive, wires up alerts, and streams several servers into one place.

TL;DR

  • Install Netdata with the official kickstart script (one command)
  • Bind the dashboard to 127.0.0.1 so it's not exposed to the internet
  • Put it behind Caddy with automatic HTTPS and HTTP basic auth
  • Tune the database and disable machine learning to fit a small VPS
  • Configure health alerts to Discord, ntfy, Slack, or email
  • Stream metrics from other servers into one parent node
  • Optionally claim nodes to Netdata Cloud for a unified view without exposing anything

Total time: around 20 minutes.

What You Need

  • A VPS running Ubuntu 22.04 or 24.04 with at least 1 GB RAM
  • A domain you can add DNS records to (for HTTPS access)
  • Ports 80 and 443 open (Let's Encrypt needs them)
  • Root or sudo access
  • Optional: one or more extra servers you want to monitor from the same dashboard

Netdata is heavier than a hub-and-agent tool like Beszel - it collects far more, far faster. On a 1 GB box it's comfortable once tuned. On 512 MB it works but you'll want to trim collectors aggressively.

Step 1: Install Netdata

The official kickstart script detects your distro, adds the repository, installs the native package, and starts the service. It's the recommended path and handles updates cleanly:

wget -O /tmp/netdata-kickstart.sh https://get.netdata.cloud/kickstart.sh sh /tmp/netdata-kickstart.sh --stable-channel --disable-telemetry

The --stable-channel flag pins you to stable releases instead of nightlies, and --disable-telemetry opts out of anonymous usage statistics. The installer asks nothing else once those flags are set.

When it finishes, confirm the service is up:

sudo systemctl status netdata

At this point Netdata is already collecting. If your firewall allowed it, http://YOUR_VPS_IP:19999 would show the full dashboard right now - which is exactly the problem we fix next.

Do not leave port 19999 open to the internet. The default dashboard has no authentication and exposes detailed system internals - running processes, open ports, mounted filesystems, and more - to anyone who finds it. Close it before you do anything else.

Step 2: Bind the Dashboard to Localhost

Netdata ships an edit-config helper that copies the stock config into place before you edit it, so you never lose the defaults. Open the main config:

cd /etc/netdata sudo ./edit-config netdata.conf

Find the [web] section (or add it) and set the bind address to loopback only:

[web] bind to = 127.0.0.1 default port = 19999

Save and restart:

sudo systemctl restart netdata

Now the dashboard only answers on the machine itself. If UFW is your firewall, make sure 19999 is not open, and lock it down to the essentials:

sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable

Verify Netdata is no longer listening on the public interface:

sudo ss -tlnp | grep 19999

You should see 127.0.0.1:19999, not 0.0.0.0:19999.

Step 3: Reverse Proxy With Caddy and Basic Auth

To reach the dashboard from a browser with HTTPS, put Caddy in front of it. First point a subdomain at your VPS. In your DNS provider, add an A record (and AAAA if you use IPv6):

netdata.example.com → YOUR_VPS_IPV4

Install Caddy from its official repository:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \ | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \ | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update sudo apt install -y caddy

Generate a bcrypt hash for the password you want to protect the dashboard with:

caddy hash-password --plaintext 'your-strong-password-here'

Copy the resulting hash. Then edit /etc/caddy/Caddyfile:

netdata.example.com { encode zstd gzip basic_auth { admin <paste-the-bcrypt-hash-here> } reverse_proxy 127.0.0.1:19999 }

Reload Caddy:

sudo systemctl reload caddy

Open https://netdata.example.com, log in as admin with your password, and the full real-time dashboard loads over HTTPS. Caddy fetches the Let's Encrypt certificate automatically on first request.

Basic auth is the minimum. If this is a personal box, the cleaner option is to skip the public hostname entirely and reach 127.0.0.1:19999 over a private network. Our Tailscale guide and WireGuard guide both work well for this - bind Netdata to the VPN interface instead of loopback and it's never on the public internet at all.

Step 4: Tune Netdata for a Small VPS

Out of the box Netdata is generous with resources. On a big server that's fine; on a 1 GB VPS you'll want to rein it in. The three biggest levers are the collection interval, the database size, and machine learning.

Reopen the main config:

cd /etc/netdata sudo ./edit-config netdata.conf

Set a slower base interval and cap the on-disk database:

[db] mode = dbengine update every = 2 dbengine tier 0 disk space MB = 256 [ml] enabled = no

What each does:

  • update every = 2 collects every two seconds instead of one. You lose a little granularity and roughly halve CPU use. On a busy box, 5 is also reasonable.
  • dbengine tier 0 disk space MB = 256 caps the metrics database at 256 MB. Netdata compresses aggressively, so that still holds days of history for a typical VPS.
  • [ml] enabled = no turns off the anomaly-detection engine. It's clever, but it trains models continuously and is the single biggest CPU consumer on a small machine. Disable it unless you specifically want anomaly bands.

You can also disable collectors you don't need. List what's running under the Netdata Monitoring section of the dashboard, then turn off individual plugins in /etc/netdata/edit-config python.d.conf or the relevant *.conf. Restart after any change:

sudo systemctl restart netdata

Check the effect with Netdata's own metrics - it monitors itself under the "Netdata Monitoring" charts, so you can watch its RAM and CPU footprint shrink live.

Step 5: Configure Health Alerts

Netdata ships with hundreds of preconfigured alarms - disk filling up, RAM pressure, a service going down, TCP resets climbing. They're on by default; you just need to point them at a notification channel.

Edit the notification config:

cd /etc/netdata sudo ./edit-config health_alarm_notify.conf

For Discord, find and set these lines:

SEND_DISCORD="YES" DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/XXX/YYY" DEFAULT_RECIPIENT_DISCORD="alerts"

For ntfy, Slack, Telegram, or email the pattern is identical - flip the SEND_* switch to YES and fill in the webhook or credentials. The file is heavily commented with the exact variable names for each channel.

Restart Netdata, then send a test notification to confirm the pipe works end to end:

sudo systemctl restart netdata sudo -u netdata /usr/libexec/netdata/plugins.d/alarm-notify.sh test

You should get a batch of test messages (warning, critical, clear) in your channel within a few seconds. If nothing arrives, the webhook URL or a SEND_* flag is wrong.

To tweak a threshold, alarms live in /etc/netdata/health.d/. For example, to change when the disk-space warning fires, edit disks.conf and adjust the warn and crit lines, then run sudo netdatacli reload-health to apply without a full restart.

Step 6: Stream Multiple Servers Into One Parent

Netdata's best-kept feature is streaming: child nodes ship their metrics to a parent node in real time, so you get one dashboard for your whole fleet without exposing every box. The parent stores the history; the children can run nearly stateless.

First, generate an API key on the parent - any UUID works:

uuidgen

On the parent VPS, open the streaming config and register the key:

cd /etc/netdata sudo ./edit-config stream.conf [<paste-the-uuid-here>] enabled = yes default memory mode = dbengine health enabled by default = auto

On each child server (install Netdata there first with the same kickstart command), edit its stream.conf and point it at the parent:

[stream] enabled = yes destination = tcp:PARENT_IP:19999 api key = <the-same-uuid>

Restart Netdata on both ends:

sudo systemctl restart netdata

Within a few seconds the child appears in the parent's dashboard node picker (top-left dropdown), streaming live. Because the parent holds the data, you can set the children to [db] mode = ram in their own netdata.conf to keep them tiny.

Stream over a private network, not the public internet. The streaming port is 19999 - the same one you just closed. Run the traffic across Tailscale or WireGuard and set destination = tcp:PARENT_TAILSCALE_IP:19999, or open the port only from the child's IP with ufw allow from CHILD_IP to any port 19999. Never expose it broadly.

Step 7 (Optional): Claim Nodes to Netdata Cloud

If you'd rather not manage a public dashboard or a parent node at all, Netdata Cloud gives you a hosted, authenticated view of every node - free for the core features. Your metrics still live on your servers; the Cloud only proxies queries to them on demand.

Claim a node from its command line:

sudo netdata-claim.sh -token=YOUR_TOKEN -rooms=YOUR_ROOM_ID -url=https://app.netdata.cloud

You get the token and room ID from the "Connect Nodes" screen in the Netdata Cloud UI. Once claimed, you can keep the local dashboard bound to 127.0.0.1 and never expose port 19999 publicly - the Cloud handles access and authentication for you. It's the lowest-maintenance option for a multi-server setup.

Troubleshooting

Caddy can't get a certificate. DNS hasn't propagated or ports 80/443 are blocked. Confirm with dig +short netdata.example.com and check your provider's firewall.

Dashboard shows "no data" behind the proxy. Netdata is bound to loopback but Caddy is proxying to the wrong address. Confirm reverse_proxy 127.0.0.1:19999 matches the bind to value in netdata.conf.

Netdata is eating CPU on a small box. Machine learning is the usual culprit. Set [ml] enabled = no and raise update every to 2 or 5, then restart.

A child won't stream to the parent. The API key must match exactly on both ends, and the parent must be reachable on port 19999 from the child. Check journalctl -u netdata -n 50 on the child for a STREAM connection error.

Alert test sends nothing. A SEND_* flag is still NO or the webhook URL is malformed. Re-run alarm-notify.sh test after fixing and watch for errors in the output.

Going Further

  • Pin the version and enable auto-updates carefully. The kickstart installer sets up a daily update timer. On a production box, review the release notes before letting it jump major versions.
  • Layer with an uptime checker. Netdata watches what's happening inside a server; Uptime Kuma watches whether it's reachable from the outside. Run both.
  • Compare with the lightweight option. If per-second granularity is overkill and you just want clean charts across a fleet, Beszel idles at a fraction of the RAM.
  • Graduate to Prometheus. Netdata can export metrics in Prometheus format from /api/v1/allmetrics?format=prometheus. If you outgrow it, our Grafana and Prometheus on a VPS guide picks up from there.

That's it. One command to install, one config change to secure it, and a real-time window into every server you run.


Need a VPS with the headroom to run real-time monitoring? Our Linux plans include fast NVMe storage, IPv6, and a generous network allowance. See the options.