Netdata, No Cloud: Local Parent/Child Streaming for a Homelab

Netdata, No Cloud: Local Parent/Child Streaming for a Homelab
Photo by Antoni Shkraba Studio: https://www.pexels.com/photo/close-up-shot-of-a-person-using-a-laptop-5475750/

You don’t need Netdata Cloud to get a single, local dashboard for multiple servers. This guide shows how I run a parent/collector on demoparent1.int.domain.com, stream three+ child nodes (like demochild1.int.domain.com) to it, keep the Web UI behind Nginx Proxy Manager at https://netdata.int.domain.com, and set sane resource limits on the children.

We’ll keep apps and cgroups metrics on every host (useful when you run Docker), cap each child at 0.5 CPU and 512 MB RAM, and store long-term history on the parent with tiered retention.


What you get

  • A local single-pane dashboard on your parent (no external SaaS).
  • Each child uses minimal resources (0.5 CPU, 512 MB RAM, no local DB).
  • Tiered retention on the parent:
    • 1s resolution for 7 days
    • 1m resolution for 1 month
    • 1h resolution for 1 year
    • Daily resolution for 3 years
      Netdata’s built-in tiers are per-second, per-minute, per-hour, and then daily/weekly. A “2-hour” tier isn’t standard; daily is the efficient long-term tier. You can keep hourly for longer by allocating more disk to Tier 2, but daily is the recommended long tail. learn.netdata.cloud+1

Prereqs

  • Ubuntu hosts with Docker Engine + Compose (v2+).
  • Internal DNS for:
    • demoparent1.int.domain.com → parent host IP
    • demochildX.int.domain.com → child host IPs
    • netdata.int.domain.com → your Nginx Proxy Manager frontend for the Web UI only
      Note: streaming is raw TCP to the parent, not HTTP through NPM. Keep the stream destination set to demoparent1.int.domain.com:19999 (or VPN/CF TCP tunnel if you’re crossing networks). learn.netdata.cloud+1

1) Parent / Collector on demoparent1.int.domain.com

Folder layout

mkdir -p ~/netdata-parent/{config,lib,cache}
cd ~/netdata-parent
uuidgen # copy this API key; used by children and parent ACL

docker-compose.yml (parent)

Bind mounts for easy backups and edits.

services:
netdata:
image: netdata/netdata:stable
container_name: netdata-parent
hostname: demoparent1.int.domain.com
network_mode: host
pid: host
cap_add: [ "SYS_PTRACE"
]
security_opt: [ "apparmor:unconfined" ]
environment:
- DO_NOT_TRACK=1
volumes:
- ./config:/etc/netdata
- ./lib:/var/lib/netdata
- ./cache:/var/cache/netdata
# Host introspection
- /etc/passwd:/host/etc/passwd:ro
- /etc/group:/host/etc/group:ro
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /etc/os-release:/host/etc/os-release:ro
# Container metrics on the parent
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped

config/stream.conf (parent)

The section name is the API key. Allow only your LAN while testing, then tighten.

[YOUR-UUID-API-KEY-HERE]
enabled = yes
allow from = 10.0.0.0/8 192.168.0.0/16

Netdata expects the literal UUID in brackets, not [API_KEY = …]. Netdata Community Forums

config/netdata.conf (parent)

Enable dbengine with 4 tiers and time-based retention. Netdata 1.46+ supports explicit retention time per tier; you can optionally add size caps per tier if you want to bound disk usage. learn.netdata.cloud

[global]
run as user
= netdata

[db]
mode
= dbengine
storage tiers = 4

# Tier 0: per-second
dbengine tier 0 retention time = 7
d

# Tier 1: per-minute
dbengine tier 1 retention time = 31
d

# Tier 2: per-hour
dbengine tier 2 retention time = 1
y

# Tier 3: daily (Netdata’s next built-in tier)
dbengine tier 3 retention time = 3
y

# Optional size caps (tune to your disk):
# dbengine tier 0 retention size = 8GiB
# dbengine tier 1 retention size = 16GiB
# dbengine tier 2 retention size = 32GiB
# dbengine tier 3 retention size = 16GiB

Start it:

docker compose up -d
# UI: http://demoparent1.int.domain.com:19999 (or via NPM: https://netdata.int.domain.com)

Tip: You can see retention/time vs space charts under Netdata → dbengine retention on the dashboard to confirm your policy. learn.netdata.cloud


2) Children on demochild1.int.domain.com (repeat per node)

Each child has no local DB, but still collects apps and cgroups for useful per-process and container stats. We cap the container at 0.5 CPU and 512 MB RAM.

Folder layout

mkdir -p ~/netdata-child/{config,lib,cache}
cd ~/netdata-child

docker-compose.yml (child)

version: "3.8"

services:
netdata:
image: netdata/netdata:stable
container_name: netdata-child
hostname: ${HOSTNAME:-demochild1.int.domain.com}
network_mode: host
pid: host
cap_add: [ "SYS_PTRACE"
]
security_opt: [ "apparmor:unconfined" ]

# Resource limits: minimal but healthy for Docker hosts
cpus: "0.5"
mem_limit: 512m
pids_limit: 256
cpu_shares: 128

environment:
- DO_NOT_TRACK=1

volumes:
- ./config:/etc/netdata
- ./lib:/var/lib/netdata
- ./cache:/var/cache/netdata
- /etc/passwd:/host/etc/passwd:ro
- /etc/group:/host/etc/group:ro
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /etc/os-release:/host/etc/os-release:ro
- /var/run/docker.sock:/var/run/docker.sock:ro

restart: unless-stopped

config/netdata.conf (child)

Keep collectors you care about; disable local persistence.

[global]
# 1-second collection keeps high-res data flowing to the parent
update every = 1

[db]
mode = none # no local TSDB on children

[plugins]
cgroups = yes
apps = yes
# Optional: enable/disable others as needed (python.d, go.d, charts.d, etc.)

config/stream.conf (child)

Stream directly to the parent, not the NPM hostname.

[stream]
enabled = yes
destination = demoparent1.int.domain.com:19999
api key
= YOUR-UUID-API-KEY-HERE
sending compress = yes
timeout seconds = 60

Restart and check logs:

docker compose up -d
docker logs -f netdata-child

Parent-child streaming reference and exact keys/filenames are documented here. learn.netdata.cloud+1



3) Putting the Web UI behind Nginx Proxy Manager

Point netdata.int.domain.com at your parent’s Netdata on :19999. Leave the child→parent stream going to demoparent1.int.domain.com:19999 directly (LAN/VPN). If you must traverse the internet, prefer VPN or a Cloudflare TCP tunnel for the stream; the HTTP tunnel is for the Web UI only. learn.netdata.cloud


4) Sizing and retention notes

  • With Netdata 1.46+ you can set time-based retention per tier (dbengine tier N retention time = …). You can also combine a time limit with a size cap per tier to prevent runaway disk usage. Start time-based, then add size caps once you see actual usage. learn.netdata.cloud
  • Tiers are fixed resolutions: per-second (T0), per-minute (T1), per-hour (T2), then daily/weekly. There isn’t a native 2-hour tier. To keep more hourly history, raise T2’s time and, if needed, size. For a very long tail, daily is far more storage-efficient. netdata.cloud+1
  • More tiers = slightly more memory. Three or four tiers are typical. learn.netdata.cloud

5) Quick troubleshooting

  • On the parent, verify it’s listening and showing stream info:curl -fsS http://127.0.0.1:19999/api/v1/stream/info | jq .
  • If a child shows 403 denied or the parent returns 404 for stream info, check:
    • Parent config/stream.conf section header is the literal UUID.
    • Parent has your LAN in allow from.
    • Children use the parent’s real host/IP, not the NPM hostname. Netdata Community Forums

  • Retention & tiers (time-based settings, tiers, cache): Netdata Database Configuration Reference. learn.netdata.cloud
  • Long-term storage & multi-tier overview: Netdata blog: Long-Term Data Storage and Retention. netdata.cloud+1
  • Parent/child streaming reference & examples: Learn Netdata. learn.netdata.cloud+1
  • Cloudflare Tunnels for UI + TCP for streams: Netdata guide. learn.netdata.cloud
  • Netdata Community Edition (Open Source): project page. netdata.cloud+1