Cutting Netdata's CPU use on a small Docker droplet

Netdata's defaults ate half the CPU of an idle 2 vCPU droplet running self-hosted Supabase in Docker. Here's which settings caused it, how to turn them down, and what you give up.

A rack server in front of a monitoring chart whose green CPU trace runs high and jagged, drops off a cliff, and then holds a low flat line.

Netdata is a fantastic monitoring tool: install it, and within seconds you get thousands of real-time charts with zero configuration. That “zero configuration” is also the catch. Out of the box, Netdata collects everything it can find, every second, and runs machine learning on top of it. On a big server you won’t notice. On a small VM running a dozen Docker containers, you will.

This post walks through what happened when I installed Netdata on a small droplet running self-hosted Supabase, why the CPU usage jumped the way it did, and the three configuration changes that brought it back down.

The setup

The machine is a DigitalOcean droplet, web-1:

  • 2 vCPU / 4 GB RAM ($24/month)
  • nginx
  • Docker running a self-hosted Supabase stack with Postgres

Here’s what was running in Docker:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e21850ea80 supabase/storage-api:v1.74.0 "docker-entrypoint.s…" 21 hours ago Up 21 hours (healthy) 5000/tcp supabase-storage
068c167f6e29 supabase/edge-runtime:v1.76.2 "edge-runtime start …" 21 hours ago Up 21 hours (healthy) supabase-edge-functions
b2c7b73dffda supabase/postgres-meta:v0.99.0 "docker-entrypoint.s…" 21 hours ago Up 21 hours (healthy) 8080/tcp supabase-meta
962e4dd6058a supabase/gotrue:v2.196.0 "auth" 21 hours ago Up 21 hours (healthy) supabase-auth
e9a9b422aa4e supabase/supavisor:2.9.12 "/usr/bin/tini -s -g…" 21 hours ago Up 21 hours (healthy) 127.0.0.1:5432->5432/tcp, 127.0.0.1:6543->6543/tcp supabase-pooler
f06c07d6c7f3 postgrest/postgrest:v14.17 "postgrest" 21 hours ago Up 21 hours (healthy) 3000/tcp supabase-rest
3839d64f77de envoyproxy/envoy:v1.39.1 "/bin/sh /docker-ent…" 21 hours ago Up 21 hours (healthy) 127.0.0.1:8000->8000/tcp, 10000/tcp supabase-envoy
b5a7fbe386a5 supabase/postgres:17.6.1.136 "docker-entrypoint.s…" 21 hours ago Up 21 hours (healthy) 5432/tcp supabase-db
a5f091fd76d5 supabase/studio:2026.09.07-sha-7996410 "docker-entrypoint.s…" 21 hours ago Up 21 hours (healthy) 3000/tcp supabase-studio
8e043f08d222 darthsim/imgproxy:v3.31.4 "entrypoint.sh imgpr…" 21 hours ago Up 21 hours (healthy) 8080/tcp supabase-imgproxy

Ten containers, all healthy, and no traffic at all: no visitors, no queries, no background jobs. An idle box.

The problem: 50% CPU on an idle server

After installing Netdata with its defaults, CPU usage on the droplet climbed to around 50%. Nothing else had changed. Netdata alone was consuming roughly a full vCPU just to watch a server doing nothing.

That sounds absurd, but it makes sense once you look at what Netdata does by default on a Docker host:

  1. It collects every metric every second. Per-second granularity is Netdata’s signature feature, and it’s also its biggest cost.
  2. It trains machine-learning models for every metric. Netdata’s anomaly detection builds and continuously updates a model per metric, on the node itself.
  3. It monitors every container, several ways at once. Each Docker container is a Linux cgroup, and Netdata’s cgroups.plugin reads CPU, memory, disk I/O and network stats for each one from /sys/fs/cgroup. Each container also has its own virtual network interface, which adds even more charts. On top of that, the Go-based collectors (go.d.plugin) query the Docker API for container states and the Docker engine for its own metrics.

Ten containers multiplies point 3 considerably, and point 2 then applies ML to every one of those extra metrics. Every chart you add costs CPU twice: once to collect it and once to model it.

Step 1: Slow down, turn off ML, drop the cgroups plugin (50% → 25%)

Netdata’s main config lives in /etc/netdata/netdata.conf. The recommended way to edit it is with the bundled helper, which copies the stock file into place if it doesn’t exist yet:

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

I added these settings:

[global]
update every = 5
[ml]
enabled = no
[plugins]
cgroups = no

What each one does:

  • update every = 5 tells Netdata to collect data every 5 seconds instead of every second. That alone cuts collection work to roughly a fifth for any collector that follows the global setting. Note: on recent Netdata versions this setting belongs in the [db] section; older configs used [global]. If a change doesn’t seem to take effect, check which section your version’s stock netdata.conf uses.
  • [ml] enabled = no turns off on-node machine learning. No more per-metric model training, and no anomaly scores or anomaly-rate charts.
  • [plugins] cgroups = no disables the cgroups plugin entirely, so Netdata stops walking the cgroup filesystem for every container and every systemd service.

Restart Netdata to apply:

Terminal window
sudo systemctl restart netdata

Result: CPU dropped from ~50% to ~25%. Better, but still a quarter of the machine for monitoring an idle server.

Step 2: Disable the Docker collectors in go.d (25% → 12–17%)

Disabling the cgroups plugin doesn’t stop all Docker monitoring. The Go collector bundle, go.d.plugin, has its own Docker-related modules that auto-detect Docker and start polling. They’re configured in /etc/netdata/go.d.conf:

Terminal window
cd /etc/netdata
sudo ./edit-config go.d.conf

I set it to:

enabled: yes
default_run: yes
max_procs: 0
modules:
docker: no
docker_engine: no
dockerhub: no
  • enabled: yes keeps go.d.plugin running. It still hosts useful collectors, including nginx and Postgres monitoring if you configure them.
  • default_run: yes means all other modules keep their default behaviour; only the ones listed below are changed.
  • max_procs: 0 lets the Go runtime use all available CPUs (this is the default).
  • docker: no stops the collector that queries the Docker API for container states, health and image counts.
  • docker_engine: no stops the collector that scrapes the Docker daemon’s own metrics endpoint.
  • dockerhub: no stops the collector for Docker Hub repository stats (pull counts and stars). Unlike the two above, this one does not auto-detect anything: it only runs if you give it repositories to watch, so switching it off here changed nothing I could measure. It is in the list to keep all three Docker modules in one place.

Restart again:

Terminal window
sudo systemctl restart netdata

Result: CPU settled at 12–17%.

Summary of results

Configuration CPU usage (idle droplet)
Netdata defaults ~50%
update every = 5, ML off, cgroups plugin off ~25%
+ go.d Docker collectors off ~12–17%

In total, that’s roughly a 70% reduction in Netdata’s overhead, with no change to the workload.

What you give up

None of this is free. Here’s what these settings cost you, so you can decide which ones fit your situation:

  • Per-container visibility. With the cgroups plugin and the Docker collectors off, Netdata no longer shows CPU, memory, network or disk per container, or container health states. You still see whole-system totals, so you’ll know that the box is busy but not which container is responsible. For that, docker stats on demand is a lightweight substitute.
  • Per-second resolution. At 5-second intervals, very short spikes get averaged out. For a small app server, 5 seconds is usually plenty. The upside is that each database tier now covers about five times more history on the same disk.
  • Anomaly detection. With ML off, there are no anomaly rates and no “what looks unusual right now” views. Threshold-based alerts (CPU, RAM, disk space, etc.) still work, because they don’t depend on ML.
  • systemd service breakdowns. The cgroups plugin also powers per-service charts for things like nginx or sshd running under systemd. Those disappear too.

Middle-ground options

If losing per-container charts is too much, you don’t have to go all the way:

  • Keep cgroups, but slow it down. Instead of disabling the plugin, give it its own, longer interval in netdata.conf:

    [plugin:cgroups]
    update every = 10
  • Keep one Docker collector. The docker module (container states and health) is much lighter than full cgroup metrics. You can re-enable it on its own and keep docker_engine and dockerhub off.

  • Offload ML to a parent. If you run Netdata on several machines, you can stream metrics to a central Netdata “parent” and run ML there, keeping the small nodes lean.

How to check what Netdata itself is costing you

Before and after each change, it helps to measure rather than guess:

  • top or htop, sorted by CPU, shows the netdata process and its plugins (go.d.plugin, apps.plugin, cgroups.plugin, and so on) as separate processes, so you can see which one is expensive.
  • Netdata’s own dashboard includes a “Netdata Monitoring” section that charts the agent’s own resource usage per plugin.

Change one thing at a time, restart, wait a few minutes for the numbers to settle, and compare.

Takeaway

Netdata’s defaults are tuned to impress on first run: every metric, every second, with ML on top. On a 2 vCPU droplet running a ten-container Supabase stack, that meant half the CPU went to watching an idle server. Three changes (a 5-second interval, ML off, and container-level collectors off) brought it down to 12–17%, while keeping the system-level monitoring and alerting that matter most on a small box. If you need per-container detail back, turn it on selectively rather than accepting the all-in defaults.