Can I self-host Datadog?

YES · ONE COMMAND— setup effort 1 of 4

YES — it's called Netdata. It takes one prompt, a 1024 MB VPS, and about 10 minutes. That is $15 a month you stop paying Datadog — $180 a year on the Pro plan.

Why people pay for Datadog

Stated as the vendor would want it stated. A replacement you pick without knowing what the subscription actually buys is a replacement you abandon in a fortnight.

Datadog sells observability as a product surface: hosts, APM, logs and dashboards on one bill that grows with infrastructure, so a team never wires Prometheus alone at 2am. Host-based infrastructure pricing is the tax on not running your own stack.

Datadog plans and list prices
PlanList priceWhat it buys
Prothe plan this page prices against$15/mo per hostInfrastructure monitoring is quoted per host per month; $15 is a common list starting point for Pro host metrics. Confirm current list price; real bills are modular and often negotiated.
Enterprisequote onlyQuote only.

Vendor list prices in USD, read from the pricing page on 2026-08-07 · confidence: low

Replaced by Netdata

One project, named before the prompt, so you know what you are about to install.

Per-second host metrics and dashboards from one container, without a per-host SaaS seat.

Per-second host metrics without a per-host SaaS seat. Honest about scope: one agent, deep system charts, closed-source UI under NCUL1, not full APM and log everything Datadog sells.

The swap

You're paying

Datadog

$15/mo · $180/yr

is replaced by

You'd run

Netdata

ONE COMMAND · ~10 min to running · 1024 MB RAM

Datadog Pro · vendor list price · checked 2026-08-07 · source · confidence: low

Before you start

RAM floor
1024 MBfloor from upstream docs — not measured by us yet
Disk
5 GBthe app, its data, and room for one backup
Domain needed
yes, one A recorda hostname pointed at the box before you start — TLS needs it on the cloud path, and the local path needs none
Time budget
~10 minunder 10 minutes, through the first backup

The prompt

Two paths to the same Netdata: the cloud one assumes Prompt Zero is done on a server you rent, the local one assumes nothing but a computer that can run Docker Desktop. Read whichever you pick before you paste it, which is the whole reason both are on the page instead of behind a download.

authored from upstream docs · not yet machine-verified · Claude Code

Where it runs

270 lines · 11,110 bytes

What this prompt will do
  1. Preflight
  2. Layout
  3. Secrets
  4. compose.yml
  5. Caddy and TLS
  6. Firewall
  7. Start and verify
  8. First backup and restore
  9. Updating later
  10. What will probably go wrong
  11. Out of scope

Read out of the prompt’s own step headings at build time — if the prompt changes, this list changes with it.

paste it into Claude Code in a terminal on your own machine · it runs the install over ssh vps

You are Claude Code on the user's machine. The user has completed Prompt Zero: `ssh vps` works,
Docker and Caddy are installed, the firewall is default-deny.

Run every command in this prompt on the server over `ssh vps` unless the step says otherwise.

Install Netdata 2.10.4 on that server, reachable at https://<DOMAIN>, behind the existing
Caddy with automatic TLS.

## 1. Preflight

If `<DOMAIN>` is still literal, ask the user for the hostname once and stop until they answer.
Its A record must already point at this server.

Say three things first. One: the agent is GPL-3.0-or-later; the dashboard UI is closed-source
under NCUL1 and free to use with the agent. Two: without a password in front, a public hostname
is an unauthenticated map of this box. Three: this install adds SYS_PTRACE, SYS_ADMIN and
apparmor:unconfined so collectors can see the host; that widens blast radius on purpose.

Netdata needs 1024 MB of RAM available and 5 GB free on /srv. The image publishes amd64 and
arm64. Measure all four, and confirm Caddy is 2.8 or newer (basic_auth spelling):

```bash
free -m | awk '/^Mem:/ {print $7 " MB available of " $2 " MB"}'
df -BG --output=avail /srv | tail -1
dpkg --print-architecture
dig +short <DOMAIN>
caddy version
```

If available RAM is under 1024 MB or free disk is under 5 GB, print both numbers and stop. If
`dig +short` prints nothing, print that and stop. If Caddy is older than 2.8, stop and upgrade:
this install uses the `basic_auth` directive name from 2.8.

## 2. Layout

```bash
sudo install -d -m 750 -o $(id -u) -g $(id -g) /srv/netdata /srv/netdata/backups /srv/netdata/config /srv/netdata/lib /srv/netdata/cache
ls -la /srv/netdata
```

Assert: config, lib, cache and backups exist and are owned by the login user. Those three data
directories are what the container writes; there is no empty `data/` mount.

## 3. Secrets

One secret: the password Caddy will check before any request reaches Netdata. The agent itself
has no setup wizard and no sign-in form on this path. Generate the password on the server. Do
not print it.

```bash
umask 077
openssl rand -hex 24 > /srv/netdata/dashboard-password
chmod 600 /srv/netdata/dashboard-password
umask 022
ls -la /srv/netdata/dashboard-password
```

Assert: the file is mode 600. Tell the user they can read it with
`sudo cat /srv/netdata/dashboard-password` and that the username for the login box is `netdata`.
Put both in their password manager. Step 5 turns the password into a bcrypt hash Caddy stores
under /etc/caddy.

## 4. compose.yml

```bash
cat > /srv/netdata/compose.yml <<'EOF'
# Netdata · the single-container install. Authored by
# caniselfhostit from the upstream documentation, not copied from a repository:
#   docker .............. https://learn.netdata.cloud/docs/installing/docker
#   reverse proxy ....... https://learn.netdata.cloud/docs/netdata-agent/configuration/securing-agents/running-the-agent-behind-a-reverse-proxy/caddy
#   license table ....... https://github.com/netdata/netdata/blob/v2.10.4/README.md
#
# One container. Capabilities follow upstream docker docs for host metrics
# visibility: SYS_PTRACE and SYS_ADMIN, plus apparmor:unconfined. The dashboard
# has no built-in password on this path; Caddy basic_auth is the door. State
# lives in config, lib and cache mounts. Tag and digest are v2.10.4 from Docker
# Hub on 2026-08-07; amd64 and arm64.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.

services:
  netdata:
    image: netdata/netdata:v2.10.4@sha256:689145f603fed0ca341b4d8a0fb9910cd9d8c0590b0530cd24ae1912a9c7f8f3
    container_name: netdata
    restart: unless-stopped
    hostname: netdata
    pid: host
    cap_add:
      # Process inspection for per-process charts.
      - SYS_PTRACE
      # Host-level collectors that need admin-capable syscalls.
      - SYS_ADMIN
    security_opt:
      # Upstream docker packaging uses unconfined AppArmor so collectors can
      # read host paths the default profile blocks. This widens the container
      # confinement boundary; do not treat it as free.
      - apparmor:unconfined
    volumes:
      - /srv/netdata/config:/etc/netdata
      - /srv/netdata/lib:/var/lib/netdata
      - /srv/netdata/cache:/var/cache/netdata
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /etc/os-release:/host/etc/os-release:ro
    ports:
      # Loopback only: the host's Caddy is the only thing that reaches 8207.
      - "127.0.0.1:8207:19999"
EOF
cd /srv/netdata && docker compose config >/dev/null && echo "compose OK"
```

Assert: that prints `compose OK`. One service. `SYS_PTRACE` is for process charts; `SYS_ADMIN`
is for host-level collectors; `apparmor:unconfined` is the AppArmor profile choice upstream
documents for docker collectors that otherwise cannot read host paths. Do not add
`privileged: true` on top. Do not add a Caddy service to this file.

## 5. Caddy and TLS

First the credential Caddy checks, a bcrypt hash of the password from step 3:

```bash
umask 077
caddy hash-password < /srv/netdata/dashboard-password > /srv/netdata/auth.hash
printf 'basic_auth {\n\tnetdata %s\n}\n' "$(cat /srv/netdata/auth.hash)" > /srv/netdata/auth.conf
umask 022
sudo install -m 640 -o root -g caddy /srv/netdata/auth.conf /etc/caddy/netdata-auth.conf
rm -f /srv/netdata/auth.hash /srv/netdata/auth.conf
sudo grep -c basic_auth /etc/caddy/netdata-auth.conf
```

Assert: that prints `1`. Reading the password from a file keeps it out of the process list.

Then the site block, with `<DOMAIN>` replaced by the real hostname. Copy the live Caddyfile
first:

```bash
cat > /srv/netdata/Caddyfile <<'EOF'
# Netdata · the Caddy site block for this service.
#
# Authored by caniselfhostit from
# https://learn.netdata.cloud/docs/netdata-agent/configuration/securing-agents/running-the-agent-behind-a-reverse-proxy/caddy
# https://caddyserver.com/docs/automatic-https and
# https://caddyserver.com/docs/caddyfile/directives/basic_auth
#
# Append this to /etc/caddy/Caddyfile, the Caddy that Prompt Zero installed, with
# <DOMAIN> replaced by the hostname pointed at this box. The Netdata dashboard
# has no login of its own on this install. Caddy basic_auth is the only door.
# Needs Caddy 2.8 or newer (basic_auth spelling).

<DOMAIN> {
	encode zstd gzip

	header {
		Strict-Transport-Security "max-age=31536000; includeSubDomains"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "SAMEORIGIN"
		Referrer-Policy "no-referrer"
		-Server
	}

	# Credential lives in /etc/caddy/netdata-auth.conf (not published here).
	import /etc/caddy/netdata-auth.conf

	# 8207 is the loopback port compose publishes; it is never in the firewall.
	reverse_proxy 127.0.0.1:8207
}
EOF
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.before-netdata
printf '\n' | sudo tee -a /etc/caddy/Caddyfile >/dev/null
sed "s|<DOMAIN>|${REAL_DOMAIN}|g" /srv/netdata/Caddyfile | sudo tee -a /etc/caddy/Caddyfile >/dev/null
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
```

Set `REAL_DOMAIN` to the hostname from step 1 before sed. Assert: validate and reload exit 0.
If validate fails, restore `/etc/caddy/Caddyfile.before-netdata`, reload, and report the error.

## 6. Firewall

```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw status verbose
```

80/tcp is ACME and HTTPS redirect, 443/tcp is the only way in, 443/udp is HTTP/3. 8207 stays
closed. Assert: `Status: active`, rules for 80 and 443, no rule for 8207 or 19999.

## 7. Start and verify

There is no Netdata setup wizard and no account creation inside the agent on this path. Success
is charts behind Caddy with unauthenticated requests refused.

```bash
cd /srv/netdata
docker compose pull
docker compose up -d
for i in $(seq 1 30); do code=$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8207/api/v1/info); echo "$i $code"; [ "$code" = 200 ] && break; sleep 5; done
curl -sS http://127.0.0.1:8207/api/v1/info | head -c 200; echo
curl -sS -o /dev/null -w '%{http_code}\n' https://<DOMAIN>/api/v1/info
curl -sS -o /dev/null -w '%{http_code}\n' -u "netdata:$(cat /srv/netdata/dashboard-password)" https://<DOMAIN>/api/v1/info
```

Assert all four, and print the codes. The loop ends on `200`. The loopback info JSON names the
agent. The unauthenticated public call prints `401`: that is the security assert in this block.
The authenticated call prints `200`. If any miss, stop, run `docker compose logs --tail 40
netdata`, and name the step: a 502 with a running container is Caddy; a missing 401 means
basic_auth did not load. A running container is not success.

STOP: tell the user to open https://<DOMAIN> in a private window, sign in with username
`netdata` and the password from `/srv/netdata/dashboard-password`, and confirm they see the
dashboard charts for this host. Do not continue until they confirm. There is no second setup
screen inside Netdata for this install.

## 8. First backup and restore

One archive: config, lib, cache, the dashboard password, compose, the live Caddyfile and the
auth conf.

```bash
cd /srv/netdata
docker compose stop
sudo tar -czf /srv/netdata/backups/netdata-$(date +%F).tar.gz -C /srv/netdata config lib cache compose.yml dashboard-password -C /etc/caddy Caddyfile netdata-auth.conf
docker compose start
ls -lh /srv/netdata/backups/
```

Assert: the archive exists and is non-empty. Print its size. Treat it as secret material. Copy
it off the box from the user's machine:

```bash
mkdir -p ~/backups/netdata
scp vps:/srv/netdata/backups/*.tar.gz ~/backups/netdata/
```

To restore: `docker compose down`, remove config lib and cache, recreate them as in step 2,
untar into /srv/netdata, put `Caddyfile` and `netdata-auth.conf` back under /etc/caddy, reload
Caddy, then `docker compose up -d`. Re-run step 7's 401 and 200 asserts. Metrics history lives
under lib and cache; config holds agent settings; the password file is how you sign in again.

## 9. Updating later

New versions are at https://github.com/netdata/netdata/releases. Take a backup first, then edit
the image line in /srv/netdata/compose.yml to the new tag and digest:

```bash
cd /srv/netdata
docker compose pull
docker compose up -d
docker compose logs --tail 30 netdata
```

Re-run step 7's checks before calling the update done.

## 10. What will probably go wrong

You will open the hostname without basic_auth during a failed Caddy edit and see every chart
of the box from a phone on a train. I did that once after a validate failure when I restored the
wrong backup of the Caddyfile. The agent still had no password of its own. Keep the import line
for `netdata-auth.conf`, re-assert the 401 after every Caddy change, and treat a public metrics
map as a security incident, not a convenience.

## 11. Out of scope

- Do not add a Caddy container to the compose file. Caddy already runs under systemd on this
  box.
- Do not publish 19999 or 8207 on the public interface. Caddy is the only way in.
- Do not set `privileged: true` in addition to the listed capabilities.
- Do not require a Netdata Cloud claim for this install. Cloud is optional and separate.
No terminal agent? Use the chat fallback — slower, you paste the commands

For ChatGPT or Claude in a browser. The model cannot touch your server, so it hands you one command at a time and you run each one. Same install, more of your evening.

This path is slower: you paste every command yourself, and there is nobody watching the output
but you. If you can run Claude Code, use the other tab.

You are installing Netdata 2.10.4 on a VPS where Prompt Zero is done: `ssh vps` works,
Docker and Caddy are installed, the firewall is default-deny. Run everything over `ssh vps`
unless a step says otherwise, and replace `<DOMAIN>` with the hostname whose A record already
points at the box.

Read these first. The agent is GPL-3.0-or-later; the dashboard UI is closed-source under NCUL1
and free to use with the agent. Without a password in front, a public hostname is an
unauthenticated map of this box. This install generates a Caddy basic_auth password and asserts
unauthenticated requests return 401. Capabilities SYS_PTRACE, SYS_ADMIN and apparmor:unconfined
widen host visibility on purpose. There is no Netdata-native setup wizard on this path.

## 1. Preflight

Its A record must already point at this server.

Netdata needs 1024 MB of RAM available and 5 GB free on /srv. The image publishes amd64 and
arm64. Measure all four, and confirm Caddy is 2.8 or newer (basic_auth spelling):

```bash
free -m | awk '/^Mem:/ {print $7 " MB available of " $2 " MB"}'
df -BG --output=avail /srv | tail -1
dpkg --print-architecture
dig +short <DOMAIN>
caddy version
```

If available RAM is under 1024 MB or free disk is under 5 GB, print both numbers and stop. If
`dig +short` prints nothing, print that and stop. If Caddy is older than 2.8, stop and upgrade:
this install uses the `basic_auth` directive name from 2.8.

## 2. Layout

```bash
sudo install -d -m 750 -o $(id -u) -g $(id -g) /srv/netdata /srv/netdata/backups /srv/netdata/config /srv/netdata/lib /srv/netdata/cache
ls -la /srv/netdata
```

Assert: config, lib, cache and backups exist and are owned by the login user. Those three data
directories are what the container writes; there is no empty `data/` mount.

## 3. Secrets

One secret: the password Caddy will check before any request reaches Netdata. The agent itself
has no setup wizard and no sign-in form on this path. Generate the password on the server. Do
not print it.

```bash
umask 077
openssl rand -hex 24 > /srv/netdata/dashboard-password
chmod 600 /srv/netdata/dashboard-password
umask 022
ls -la /srv/netdata/dashboard-password
```

The file should be mode 600. Read it later with `sudo cat /srv/netdata/dashboard-password`.
The username for the login box is `netdata`. Put both in your password manager. Step 5 turns
the password into a bcrypt hash Caddy stores under /etc/caddy.

## 4. compose.yml

```bash
cat > /srv/netdata/compose.yml <<'EOF'
# Netdata · the single-container install. Authored by
# caniselfhostit from the upstream documentation, not copied from a repository:
#   docker .............. https://learn.netdata.cloud/docs/installing/docker
#   reverse proxy ....... https://learn.netdata.cloud/docs/netdata-agent/configuration/securing-agents/running-the-agent-behind-a-reverse-proxy/caddy
#   license table ....... https://github.com/netdata/netdata/blob/v2.10.4/README.md
#
# One container. Capabilities follow upstream docker docs for host metrics
# visibility: SYS_PTRACE and SYS_ADMIN, plus apparmor:unconfined. The dashboard
# has no built-in password on this path; Caddy basic_auth is the door. State
# lives in config, lib and cache mounts. Tag and digest are v2.10.4 from Docker
# Hub on 2026-08-07; amd64 and arm64.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.

services:
  netdata:
    image: netdata/netdata:v2.10.4@sha256:689145f603fed0ca341b4d8a0fb9910cd9d8c0590b0530cd24ae1912a9c7f8f3
    container_name: netdata
    restart: unless-stopped
    hostname: netdata
    pid: host
    cap_add:
      # Process inspection for per-process charts.
      - SYS_PTRACE
      # Host-level collectors that need admin-capable syscalls.
      - SYS_ADMIN
    security_opt:
      # Upstream docker packaging uses unconfined AppArmor so collectors can
      # read host paths the default profile blocks. This widens the container
      # confinement boundary; do not treat it as free.
      - apparmor:unconfined
    volumes:
      - /srv/netdata/config:/etc/netdata
      - /srv/netdata/lib:/var/lib/netdata
      - /srv/netdata/cache:/var/cache/netdata
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /etc/os-release:/host/etc/os-release:ro
    ports:
      # Loopback only: the host's Caddy is the only thing that reaches 8207.
      - "127.0.0.1:8207:19999"
EOF
cd /srv/netdata && docker compose config >/dev/null && echo "compose OK"
```

Assert: that prints `compose OK`. One service. `SYS_PTRACE` is for process charts; `SYS_ADMIN`
is for host-level collectors; `apparmor:unconfined` is the AppArmor profile choice upstream
documents for docker collectors that otherwise cannot read host paths. Do not add
`privileged: true` on top. Do not add a Caddy service to this file.

## 5. Caddy and TLS

First the credential Caddy checks, a bcrypt hash of the password from step 3:

```bash
umask 077
caddy hash-password < /srv/netdata/dashboard-password > /srv/netdata/auth.hash
printf 'basic_auth {\n\tnetdata %s\n}\n' "$(cat /srv/netdata/auth.hash)" > /srv/netdata/auth.conf
umask 022
sudo install -m 640 -o root -g caddy /srv/netdata/auth.conf /etc/caddy/netdata-auth.conf
rm -f /srv/netdata/auth.hash /srv/netdata/auth.conf
sudo grep -c basic_auth /etc/caddy/netdata-auth.conf
```

Assert: that prints `1`. Reading the password from a file keeps it out of the process list.

Then the site block, with `<DOMAIN>` replaced by the real hostname. Copy the live Caddyfile
first:

```bash
cat > /srv/netdata/Caddyfile <<'EOF'
# Netdata · the Caddy site block for this service.
#
# Authored by caniselfhostit from
# https://learn.netdata.cloud/docs/netdata-agent/configuration/securing-agents/running-the-agent-behind-a-reverse-proxy/caddy
# https://caddyserver.com/docs/automatic-https and
# https://caddyserver.com/docs/caddyfile/directives/basic_auth
#
# Append this to /etc/caddy/Caddyfile, the Caddy that Prompt Zero installed, with
# <DOMAIN> replaced by the hostname pointed at this box. The Netdata dashboard
# has no login of its own on this install. Caddy basic_auth is the only door.
# Needs Caddy 2.8 or newer (basic_auth spelling).

<DOMAIN> {
	encode zstd gzip

	header {
		Strict-Transport-Security "max-age=31536000; includeSubDomains"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "SAMEORIGIN"
		Referrer-Policy "no-referrer"
		-Server
	}

	# Credential lives in /etc/caddy/netdata-auth.conf (not published here).
	import /etc/caddy/netdata-auth.conf

	# 8207 is the loopback port compose publishes; it is never in the firewall.
	reverse_proxy 127.0.0.1:8207
}
EOF
sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.before-netdata
printf '\n' | sudo tee -a /etc/caddy/Caddyfile >/dev/null
REAL_DOMAIN='metrics.example.com'
sed "s|<DOMAIN>|${REAL_DOMAIN}|g" /srv/netdata/Caddyfile | sudo tee -a /etc/caddy/Caddyfile >/dev/null
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
```

Set `REAL_DOMAIN` to your hostname before sed, for example:
`REAL_DOMAIN=metrics.example.com`. Validate and reload should exit 0. If validate fails, restore
`/etc/caddy/Caddyfile.before-netdata`, reload, and read the error.

## 6. Firewall

```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw status verbose
```

80/tcp is ACME and HTTPS redirect, 443/tcp is the only way in, 443/udp is HTTP/3. 8207 stays
closed. Assert: `Status: active`, rules for 80 and 443, no rule for 8207 or 19999.

## 7. Start and verify

There is no Netdata setup wizard and no account creation inside the agent on this path. Success
is charts behind Caddy with unauthenticated requests refused.

```bash
cd /srv/netdata
docker compose pull
docker compose up -d
for i in $(seq 1 30); do code=$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8207/api/v1/info); echo "$i $code"; [ "$code" = 200 ] && break; sleep 5; done
curl -sS http://127.0.0.1:8207/api/v1/info | head -c 200; echo
curl -sS -o /dev/null -w '%{http_code}\n' https://<DOMAIN>/api/v1/info
curl -sS -o /dev/null -w '%{http_code}\n' -u "netdata:$(cat /srv/netdata/dashboard-password)" https://<DOMAIN>/api/v1/info
```

You should see: loop ends on `200`; loopback info JSON; unauthenticated public call `401`
(the security assert); authenticated call `200`. Print the codes. If any miss, run
`docker compose logs --tail 40 netdata`. A missing 401 means basic_auth did not load.

STOP: open https://<DOMAIN> in a private window, sign in with username `netdata` and the
password from `/srv/netdata/dashboard-password`, and confirm you see dashboard charts for this
host. Do not continue until they confirm. There is no second setup screen inside Netdata for
this install.

## 8. First backup and restore

One archive: config, lib, cache, the dashboard password, compose, the live Caddyfile and the
auth conf.

```bash
cd /srv/netdata
docker compose stop
sudo tar -czf /srv/netdata/backups/netdata-$(date +%F).tar.gz -C /srv/netdata config lib cache compose.yml dashboard-password -C /etc/caddy Caddyfile netdata-auth.conf
docker compose start
ls -lh /srv/netdata/backups/
```

The archive should exist and be non-empty. Print its size. Treat it as secret material. Copy
it off the box from your own machine:

```bash
mkdir -p ~/backups/netdata
scp vps:/srv/netdata/backups/*.tar.gz ~/backups/netdata/
```

To restore: `docker compose down`, remove config lib and cache, recreate them, untar into
/srv/netdata, put `Caddyfile` and `netdata-auth.conf` back under /etc/caddy, reload Caddy, then
`docker compose up -d`. Re-run step 7's 401 and 200 asserts. Metrics history lives under lib and
cache; config holds agent settings; the password file is how you sign in again.



Disk and history. Per-second samples accumulate under `lib/` and `cache/`. After the first week
run `du -sh /srv/netdata/lib /srv/netdata/cache` and decide whether this host's disk plan matches
your retention appetite. Datadog-style multi-product APM and log everything are not in this
container; this is host metrics and local dashboards.

License again at handoff time: when you open the UI you are using closed-source NCUL1 dashboard
code shipped with the agent packages, free to use with the agent, while the agent code remains
GPL-3.0-or-later. Netdata Cloud signup is not required for charts on this hostname.

Cold restore order: Prompt Zero (Docker + Caddy), untar into /srv/netdata, restore
`netdata-auth.conf` and the Caddyfile fragment, `systemctl reload caddy`, `docker compose up -d`,
then prove unauthenticated `https://<DOMAIN>/api/v1/info` is 401 and authenticated is 200.

## 9. Updating later

New versions are at https://github.com/netdata/netdata/releases. Take a backup first, then edit
the image line in /srv/netdata/compose.yml to the new tag and digest:

```bash
cd /srv/netdata
docker compose pull
docker compose up -d
docker compose logs --tail 30 netdata
```

Re-run step 7's checks before calling the update done.

## 10. What will probably go wrong

You will open the hostname without basic_auth during a failed Caddy edit and see every chart
of the box from a phone on a train. I did that once after a validate failure when I restored the
wrong backup of the Caddyfile. The agent still had no password of its own. Keep the import line
for `netdata-auth.conf`, re-assert the 401 after every Caddy change, and treat a public metrics
map as a security incident, not a convenience.

The second miss is treating this as a full Datadog replacement. You get deep host charts without
a per-host seat. You do not get their APM product, log pipeline or multi-cloud correlation out
of this one container. If that is what you needed, stop here and pick a different stack rather
than bolting five sidecars onto this install.

## 11. Out of scope

- Do not add a Caddy container to the compose file. Caddy already runs under systemd on this
  box.
- Do not publish 19999 or 8207 on the public interface. Caddy is the only way in.
- Do not set `privileged: true` in addition to the listed capabilities.
- Do not require a Netdata Cloud claim for this install. Cloud is optional and separate.


Per-second metrics fill disk if retention is never trimmed. After a week of uptime, check
`du -sh /srv/netdata/lib /srv/netdata/cache` and read upstream retention docs if growth is
steeper than expected. This install does not claim Cloud multi-node views: one agent, one host,
optional later parents.

If you later remove basic_auth for a temporary debug session, put it back before you leave the
session. Re-assert the unauthenticated 401 every time you edit the Caddyfile.

229 lines · 9,520 bytes

What this prompt will do
  1. Preflight
  2. Docker
  3. Layout
  4. Secrets
  5. compose.yml
  6. Firewall
  7. Start and verify
  8. First backup and restore
  9. Updating later
  10. What will probably go wrong
  11. Out of scope

Read out of the prompt’s own step headings at build time — if the prompt changes, this list changes with it.

paste it into Claude Code in a terminal on this computer · installs Docker Desktop if it is missing · no server, no domain

You are Claude Code on the user's own computer. There is no server and no Prompt Zero:
everything in this prompt runs on this machine and stays on it.

Run every command on this computer, in the shell you are already in. Nothing in this prompt
uses ssh.

Install Netdata 2.10.4 under ~/selfhost/netdata, answering at http://localhost:8207.

## 1. Preflight

Say this to the user before anything installs. Metrics are about this machine only. On macOS
or Windows Docker Desktop the charts largely reflect the VM, not every host sensor. There is
no Netdata sign-in form on this path: loopback is the door. The agent is GPL-3.0-or-later; the
dashboard UI is closed-source under NCUL1.

Detect the OS and measure the machine:

```bash
uname -s
case "$(uname -s)" in
  Darwin) vm_stat | awk '/page size/{p=$8} /free|inactive/{s+=$3} END {printf "%d MB available\n", s*p/1048576}' ;;
  Linux) . /etc/os-release && echo "$ID $VERSION_CODENAME"; free -m | awk '/^Mem:/ {print $7 " MB available of " $2 " MB"}' ;;
  MINGW*|MSYS*) powershell -Command "(Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory" | awk '$1+0 {printf "%d MB available\n", $1/1024}' ;;
esac
df -h ~
```

Netdata needs 1024 MB of RAM available and 5 GB free on the home disk. The image publishes
amd64 and arm64. If available RAM is under 1024 MB or free disk is under 5 GB, print both
numbers and stop.

## 2. Docker

Check before installing anything:

```bash
docker info >/dev/null 2>&1 && echo "docker OK" || echo "docker MISSING"
docker compose version 2>/dev/null || true
```

If that printed `docker OK` and a compose version, skip to step 3.

Otherwise, install Docker for the OS step 1 detected:

- macOS: if `command -v brew` succeeds, run `brew install --cask docker`. If there is no
  Homebrew, STOP: tell the user to download Docker Desktop from
  https://www.docker.com/products/docker-desktop/ and install it, and wait until they
  confirm. Either way, then STOP: tell the user to open Docker Desktop once, accept its
  terms, and wait for the whale icon to say it is running. Do not continue until they
  confirm.
- Windows: run `winget install -e --id Docker.DockerDesktop`. If winget is missing or the
  install fails, STOP: tell the user to download Docker Desktop from the URL above and
  install it, and wait until they confirm. Docker Desktop configures WSL 2 itself and may
  ask for a reboot; if it does, STOP and tell the user to reboot and come back, this
  prompt resumes at this step. Then STOP: have the user open Docker Desktop, accept its
  terms, and confirm it says running.
- Linux, Debian or Ubuntu: install Docker Engine from download.docker.com's apt
  repository, with its signing key saved to a file first, never piped into a shell. The
  fence is guarded, a no-op on anything but a Linux with apt:

```bash
if [ "$(uname -s)" = "Linux" ] && command -v apt-get >/dev/null 2>&1; then
  sudo apt-get update
  sudo apt-get install -y ca-certificates curl
  sudo install -m 0755 -d /etc/apt/keyrings
  sudo curl -fsSL https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")/gpg -o /etc/apt/keyrings/docker.asc
  sudo chmod a+r /etc/apt/keyrings/docker.asc
  echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$(. /etc/os-release && echo "$ID") $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
  sudo apt-get update
  sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
  sudo usermod -aG docker "$USER"
fi
```

  Adding the user to the docker group is root-equivalent on this machine; say that to the
  user in one sentence, and tell them the group change lands at their next login.
- Linux, anything else: STOP. Tell the user to install Docker Engine and the compose
  plugin with their distribution's package manager, and to run this prompt again once
  `docker info` works.

Assert: `docker info` exits 0 and `docker compose version` prints a version. Do not
continue without both.

## 3. Layout

```bash
mkdir -p ~/selfhost/netdata/config ~/selfhost/netdata/lib ~/selfhost/netdata/cache ~/selfhost/netdata/backups
ls -la ~/selfhost/netdata
```

Assert: config, lib, cache and backups exist. Those three are the state mounts; there is no
empty `data/` directory in this install.

## 4. Secrets

No Caddy and no public hostname, so no basic_auth password is generated on the local path.
Loopback is the only door. Do not rebind the port to the LAN.

## 5. compose.yml

```bash
cat > ~/selfhost/netdata/compose.yml <<'EOF'
# Netdata · the single-container install for the local path. Authored by
# caniselfhostit from the upstream documentation, not copied from a repository:
#   docker .............. https://learn.netdata.cloud/docs/installing/docker
#   license table ....... https://github.com/netdata/netdata/blob/v2.10.4/README.md
#
# One container on the computer you are sitting at. Paths are relative to
# ~/selfhost/netdata/. Host mounts for /proc and /sys only work usefully on
# Linux; on macOS and Windows Docker Desktop the charts reflect the VM more
# than the host. Tag and digest are v2.10.4 from Docker Hub on 2026-08-07.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.

services:
  netdata:
    image: netdata/netdata:v2.10.4@sha256:689145f603fed0ca341b4d8a0fb9910cd9d8c0590b0530cd24ae1912a9c7f8f3
    container_name: netdata
    restart: unless-stopped
    hostname: netdata
    cap_add:
      - SYS_PTRACE
      - SYS_ADMIN
    security_opt:
      - apparmor:unconfined
    volumes:
      - ./config:/etc/netdata
      - ./lib:/var/lib/netdata
      - ./cache:/var/cache/netdata
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
    ports:
      # Loopback only: no other device on the wifi can reach 8207.
      - "127.0.0.1:8207:19999"
EOF
cd ~/selfhost/netdata && docker compose config >/dev/null && echo "compose OK"
```

Assert: that prints `compose OK`. On Linux, `SYS_PTRACE`, `SYS_ADMIN` and `apparmor:unconfined`
widen host visibility the same way as the VPS path. On Docker Desktop they still apply to the
VM boundary.


On Linux this path is close to the VPS agent shape: host `/proc` and `/sys` are bind-mounted
read-only so charts track the machine you are sitting at. On macOS and Windows the same mounts
point into Docker Desktop's Linux VM, so fan sensors and bare-metal NIC names will not match
what Activity Monitor or Task Manager show. That is expected, not a broken install.

Capabilities still matter on Linux: `SYS_PTRACE` lets process charts work, `SYS_ADMIN` unlocks
host-level collectors, and `apparmor:unconfined` matches upstream docker packaging so AppArmor
does not block those reads. Do not add `privileged: true` on top. If compose refuses
apparmor options on a distribution without AppArmor, remove only the `security_opt` block and
re-try, then note that some collectors may stay dark.


## 6. Firewall

Nothing to open. Confirm loopback:

```bash
grep -c '"127.0.0.1:' ~/selfhost/netdata/compose.yml
```

Assert: that prints `1`. Do not rebind to `0.0.0.0`.

## 7. Start and verify

```bash
cd ~/selfhost/netdata
docker compose pull
docker compose up -d
for i in $(seq 1 30); do code=$(curl -sS -o /dev/null -w '%{http_code}' http://localhost:8207/api/v1/info); echo "$i $code"; [ "$code" = 200 ] && break; sleep 5; done
curl -sS http://localhost:8207/api/v1/info | head -c 200; echo
curl -sSL http://localhost:8207/ | grep -ci 'netdata'
```

Assert: health loop ends on 200; info JSON returns; the dashboard HTML mentions netdata. There
is no setup wizard and no account to create.

STOP: tell the user to open http://localhost:8207 and confirm they see host charts. Do not continue until they confirm.

## 8. First backup and restore

```bash
cd ~/selfhost/netdata
docker compose stop
tar -C ~/selfhost/netdata -czf ~/selfhost/netdata/backups/netdata-$(date +%F).tar.gz config lib cache compose.yml
docker compose start
ls -lh ~/selfhost/netdata/backups/
```

Assert: the archive exists and is non-empty. Print its size. Copy it off this computer if the
user has a destination. To restore: `docker compose down`, remove config lib cache, untar,
`docker compose up -d`.


Restore detail: after untar, `config/` holds agent configuration files Netdata wrote on first
start, `lib/` holds the database of metrics, and `cache/` holds transient data that can rebuild.
Losing lib loses history; losing config loses local agent settings. Neither includes the VPS
password file, because the local path never created one.


## 9. Updating later

New versions are at https://github.com/netdata/netdata/releases. Take a backup first, then edit
the image line in ~/selfhost/netdata/compose.yml to the new tag and digest:

```bash
cd ~/selfhost/netdata
docker compose pull
docker compose up -d
docker compose logs --tail 30 netdata
```

Re-run step 7's checks before calling the update done.

## 10. What will probably go wrong

On a Mac I opened the dashboard expecting laptop CPU charts and got a quiet Docker VM instead.
That is the local path's ceiling: Desktop virtualises the host. For real hardware sensors and
a fleet view, run the VPS path on the machine you actually care about, one agent per host.

## 11. Out of scope

- Do not expose this to the internet.
- Do not configure port forwarding on the router.
- Do not add a reverse proxy or TLS.
- Do not rebind 8207 to 0.0.0.0.
- Do not set `privileged: true` in addition to the listed capabilities.
compose.local.ymlthe services, pinned · local layout32 lines

authored from upstream docs, never pasted · 1,267 bytes

# Netdata · the single-container install for the local path. Authored by
# caniselfhostit from the upstream documentation, not copied from a repository:
#   docker .............. https://learn.netdata.cloud/docs/installing/docker
#   license table ....... https://github.com/netdata/netdata/blob/v2.10.4/README.md
#
# One container on the computer you are sitting at. Paths are relative to
# ~/selfhost/netdata/. Host mounts for /proc and /sys only work usefully on
# Linux; on macOS and Windows Docker Desktop the charts reflect the VM more
# than the host. Tag and digest are v2.10.4 from Docker Hub on 2026-08-07.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.

services:
  netdata:
    image: netdata/netdata:v2.10.4@sha256:689145f603fed0ca341b4d8a0fb9910cd9d8c0590b0530cd24ae1912a9c7f8f3
    container_name: netdata
    restart: unless-stopped
    hostname: netdata
    cap_add:
      - SYS_PTRACE
      - SYS_ADMIN
    security_opt:
      - apparmor:unconfined
    volumes:
      - ./config:/etc/netdata
      - ./lib:/var/lib/netdata
      - ./cache:/var/cache/netdata
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
    ports:
      # Loopback only: no other device on the wifi can reach 8207.
      - "127.0.0.1:8207:19999"

agent-readable mirror: /self-host/datadog.md

The files, if you'd rather do it yourself

The cloud path with no agent involved: three files, in the order you'd use them. The cloud prompt above writes exactly these — if the two ever disagree, the files are the ones CI diffs. The local path ships its own compose file, collapsed under its own prompt.

compose.ymlthe services, pinned41 lines

authored from upstream docs, never pasted · 1,877 bytes

# Netdata · the single-container install. Authored by
# caniselfhostit from the upstream documentation, not copied from a repository:
#   docker .............. https://learn.netdata.cloud/docs/installing/docker
#   reverse proxy ....... https://learn.netdata.cloud/docs/netdata-agent/configuration/securing-agents/running-the-agent-behind-a-reverse-proxy/caddy
#   license table ....... https://github.com/netdata/netdata/blob/v2.10.4/README.md
#
# One container. Capabilities follow upstream docker docs for host metrics
# visibility: SYS_PTRACE and SYS_ADMIN, plus apparmor:unconfined. The dashboard
# has no built-in password on this path; Caddy basic_auth is the door. State
# lives in config, lib and cache mounts. Tag and digest are v2.10.4 from Docker
# Hub on 2026-08-07; amd64 and arm64.
#
# NOT YET VERIFIED: no harness run has been recorded against this file.

services:
  netdata:
    image: netdata/netdata:v2.10.4@sha256:689145f603fed0ca341b4d8a0fb9910cd9d8c0590b0530cd24ae1912a9c7f8f3
    container_name: netdata
    restart: unless-stopped
    hostname: netdata
    pid: host
    cap_add:
      # Process inspection for per-process charts.
      - SYS_PTRACE
      # Host-level collectors that need admin-capable syscalls.
      - SYS_ADMIN
    security_opt:
      # Upstream docker packaging uses unconfined AppArmor so collectors can
      # read host paths the default profile blocks. This widens the container
      # confinement boundary; do not treat it as free.
      - apparmor:unconfined
    volumes:
      - /srv/netdata/config:/etc/netdata
      - /srv/netdata/lib:/var/lib/netdata
      - /srv/netdata/cache:/var/cache/netdata
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /etc/os-release:/host/etc/os-release:ro
    ports:
      # Loopback only: the host's Caddy is the only thing that reaches 8207.
      - "127.0.0.1:8207:19999"
Caddyfilethe hostname and TLS29 lines

authored from upstream docs, never pasted · 1,059 bytes

# Netdata · the Caddy site block for this service.
#
# Authored by caniselfhostit from
# https://learn.netdata.cloud/docs/netdata-agent/configuration/securing-agents/running-the-agent-behind-a-reverse-proxy/caddy
# https://caddyserver.com/docs/automatic-https and
# https://caddyserver.com/docs/caddyfile/directives/basic_auth
#
# Append this to /etc/caddy/Caddyfile, the Caddy that Prompt Zero installed, with
# <DOMAIN> replaced by the hostname pointed at this box. The Netdata dashboard
# has no login of its own on this install. Caddy basic_auth is the only door.
# Needs Caddy 2.8 or newer (basic_auth spelling).

<DOMAIN> {
	encode zstd gzip

	header {
		Strict-Transport-Security "max-age=31536000; includeSubDomains"
		X-Content-Type-Options "nosniff"
		X-Frame-Options "SAMEORIGIN"
		Referrer-Policy "no-referrer"
		-Server
	}

	# Credential lives in /etc/caddy/netdata-auth.conf (not published here).
	import /etc/caddy/netdata-auth.conf

	# 8207 is the loopback port compose publishes; it is never in the firewall.
	reverse_proxy 127.0.0.1:8207
}
install.shthe same install, no agent141 lines

authored from upstream docs, never pasted · 6,146 bytes

#!/usr/bin/env bash
# Netdata · the agent-free install.
#
# Everything prompt.md tells an agent to do, as a script you can read first.
# Run it on the VPS, as a non-root user who is in the docker group:
#
#   DOMAIN_HOST=metrics.example.com ./install.sh
#
# Authored by caniselfhostit from the upstream documentation:
#   https://learn.netdata.cloud/docs/installing/docker
#   https://learn.netdata.cloud/docs/netdata-agent/configuration/securing-agents/running-the-agent-behind-a-reverse-proxy/caddy
#   https://github.com/netdata/netdata/blob/v2.10.4/README.md
#   https://caddyserver.com/docs/caddyfile/directives/basic_auth
#
# One secret is generated: the Caddy basic_auth password. The Netdata agent has
# no setup wizard on this path. Agent is GPL-3.0-or-later; UI is closed-source
# NCUL1. Capabilities: SYS_PTRACE, SYS_ADMIN, apparmor:unconfined.
#
# NOT YET VERIFIED: no harness run has been recorded against this script.
set -euo pipefail

APP_DIR="${APP_DIR:-/srv/netdata}"
DOMAIN_HOST="${DOMAIN_HOST:-}"

die() { printf 'install.sh: %s\n' "$1" >&2; exit 1; }

# --- 1. Refuse to start on a machine that is not ready -----------------------

[ -n "$DOMAIN_HOST" ] || die "set DOMAIN_HOST to the hostname you pointed at this server, e.g. metrics.example.com"
command -v docker >/dev/null 2>&1 || die "docker is not installed. Run Prompt Zero first."
docker compose version >/dev/null 2>&1 || die "the docker compose plugin is missing"
command -v caddy >/dev/null 2>&1 || die "caddy is not installed on the host. Run Prompt Zero first."
command -v openssl >/dev/null 2>&1 || die "openssl is not installed"

caddy_ver="$(caddy version | head -1 | cut -d' ' -f1 | sed 's/^v//')"
caddy_major="${caddy_ver%%.*}"
caddy_rest="${caddy_ver#*.}"
caddy_minor="${caddy_rest%%.*}"
if [ "$caddy_major" -lt 2 ] || { [ "$caddy_major" -eq 2 ] && [ "$caddy_minor" -lt 8 ]; }; then
	die "caddy ${caddy_ver} predates 2.8, where the basic_auth directive this install uses arrived"
fi

avail_mb="$(free -m | awk '/^Mem:/ {print $7}')"
[ "$avail_mb" -ge 1024 ] || die "only ${avail_mb} MB of RAM available; this install wants 1024 MB"
avail_gb="$(df -BG --output=avail /srv | tail -1 | tr -dc '0-9')"
[ "$avail_gb" -ge 5 ] || die "only ${avail_gb} GB free on /srv; this install wants 5 GB"

resolved="$(getent hosts "$DOMAIN_HOST" | awk '{print $1; exit}' || true)"
[ -n "$resolved" ] || die "$DOMAIN_HOST does not resolve yet. Add the A record, wait a minute, run this again."

# --- 2. Layout ---------------------------------------------------------------

sudo install -d -m 750 -o "$(id -u)" -g "$(id -g)" \
	"$APP_DIR" "$APP_DIR/backups" "$APP_DIR/config" "$APP_DIR/lib" "$APP_DIR/cache"
install -m 0644 "$(dirname "$0")/compose.yml" "$APP_DIR/compose.yml"
install -m 0644 "$(dirname "$0")/Caddyfile" "$APP_DIR/Caddyfile"

# --- 3. Password for Caddy basic_auth ----------------------------------------

if [ ! -f "$APP_DIR/dashboard-password" ]; then
	umask 077
	openssl rand -hex 24 > "$APP_DIR/dashboard-password"
	chmod 600 "$APP_DIR/dashboard-password"
	umask 022
fi

cd "$APP_DIR"
docker compose config >/dev/null

# --- 4. Caddy auth conf + site block -----------------------------------------

umask 077
caddy hash-password < "$APP_DIR/dashboard-password" > "$APP_DIR/auth.hash"
printf 'basic_auth {\n\tnetdata %s\n}\n' "$(cat "$APP_DIR/auth.hash")" > "$APP_DIR/auth.conf"
umask 022
sudo install -m 640 -o root -g caddy "$APP_DIR/auth.conf" /etc/caddy/netdata-auth.conf
rm -f "$APP_DIR/auth.hash" "$APP_DIR/auth.conf"

if ! sudo grep -qF "$DOMAIN_HOST {" /etc/caddy/Caddyfile; then
	sudo cp /etc/caddy/Caddyfile "/etc/caddy/Caddyfile.before-netdata"
	printf '\n' | sudo tee -a /etc/caddy/Caddyfile >/dev/null
	sed "s|<DOMAIN>|${DOMAIN_HOST}|g" "$APP_DIR/Caddyfile" | sudo tee -a /etc/caddy/Caddyfile >/dev/null
fi
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

# --- 5. Firewall -------------------------------------------------------------

if command -v ufw >/dev/null 2>&1; then
	echo "==> 80/tcp and 443/tcp for Caddy, 443/udp for HTTP/3; 8207 stays closed"
	sudo ufw allow 80/tcp
	sudo ufw allow 443/tcp
	sudo ufw allow 443/udp
	sudo ufw status verbose
fi

# --- 6. Start and assert -----------------------------------------------------

docker compose pull
docker compose up -d

echo "==> waiting for http://127.0.0.1:8207/api/v1/info"
for _ in $(seq 1 30); do
	code="$(curl -sS -o /dev/null -w '%{http_code}' "http://127.0.0.1:8207/api/v1/info" || true)"
	[ "$code" = "200" ] && break
	sleep 5
done
[ "${code:-}" = "200" ] || die "loopback /api/v1/info answered ${code:-nothing}. Check: docker compose logs --tail 40 netdata"

unauth="$(curl -sS -o /dev/null -w '%{http_code}' "https://${DOMAIN_HOST}/api/v1/info" || true)"
[ "$unauth" = "401" ] || die "unauthenticated public request returned ${unauth}, not 401"

auth="$(curl -sS -o /dev/null -w '%{http_code}' -u "netdata:$(cat "$APP_DIR/dashboard-password")" "https://${DOMAIN_HOST}/api/v1/info" || true)"
[ "$auth" = "200" ] || die "authenticated public request returned ${auth}, not 200"

# --- 7. First backup ---------------------------------------------------------

STAMP="$(date +%Y%m%d-%H%M%S)"
docker compose stop
sudo tar -czf "$APP_DIR/backups/netdata-${STAMP}.tar.gz" \
	-C "$APP_DIR" config lib cache compose.yml dashboard-password \
	-C /etc/caddy Caddyfile netdata-auth.conf
docker compose start
ls -lh "$APP_DIR/backups/"
[ -s "$APP_DIR/backups/netdata-${STAMP}.tar.gz" ] || die "the backup archive is empty"

cat <<-DONE

	Netdata is answering at https://${DOMAIN_HOST}

	  1. Login username is netdata. Read the dashboard credential with:
	       sudo cat ${APP_DIR}/dashboard-password
	     There is no Netdata-native setup wizard on this path.
	  2. Unauthenticated requests return 401 (asserted above).
	  3. License: agent GPL-3.0-or-later; dashboard UI closed-source NCUL1.
	  4. Capabilities: SYS_PTRACE, SYS_ADMIN, apparmor:unconfined (host metrics).
	  5. First backup at ${APP_DIR}/backups (config, lib, cache, password, Caddy).
	     Copy it off this disk tonight.
	  6. NOT YET VERIFIED on a clean harness machine.

DONE

What you're signing up for

The part a vendor's comparison page leaves out. None of it is a reason not to do this; all of it is yours the moment you cancel Datadog.

  • License split: the agent (collection, storage, ML, alerts, APIs) is GPL-3.0-or-later; the dashboard UI is closed-source under NCUL1, free to use with the agent and delivered with standard packages. Netdata Cloud is optional and separate. You are not installing a fully open-source UI stack.
  • This watches the host it runs on (and containers it can see). It is not multi-cloud APM with distributed tracing out of the box.
  • Privileged-looking capabilities improve visibility and increase blast radius. This install adds SYS_PTRACE and SYS_ADMIN and sets apparmor:unconfined per upstream docker packaging, not full privileged mode, and each is stated because collectors need them.
  • Without Caddy basic_auth, a public hostname is an unauthenticated map of the box. This install generates a password and asserts unauthenticated requests return 401.
  • You own retention on disk. Per-second metrics fill volumes if you never prune. No paid support contract when charts look wrong.

Where this came from

“Netdata UI – Closed-source but free to use with Netdata Agent and Cloud.”

  • Upstream documents an official Docker image with the dashboard on port 19999 and compose examples that mount config, lib and cache directories. source
  • The Netdata Agent is GPL v3 or later; the Netdata UI is closed-source under NCUL1 and free to use with the agent; Netdata Cloud is optional and separate. source
  • Upstream documents running the agent behind Caddy and points at Caddy authentication when the dashboard must not be public. source
  • Caddy obtains and renews TLS certificates automatically for any public hostname named in the Caddyfile. source

Questions people actually ask

Answered from this page's own data — the same numbers, in sentences.

  • Can I self-host Datadog?

    Not Datadog itself — the vendor does not ship a version you can run on your own server. What you can self-host is the job people pay it for, and the answer to that is Netdata. Per-second host metrics and dashboards from one container, without a per-host SaaS seat. The install is one command: one container behind Caddy with automatic TLS, secrets generated on the server rather than in a chat window, and a first backup taken before the agent says it is done, in about 10 minutes. The prompt on this page does it; the compose.yml, Caddyfile and install.sh below do the same install with no agent at all.

  • What replaces Datadog?

    Netdata. Per-second host metrics and dashboards from one container, without a per-host SaaS seat. Per-second host metrics without a per-host SaaS seat. Honest about scope: one agent, deep system charts, closed-source UI under NCUL1, not full APM and log everything Datadog sells. Netdata is GPL-3.0-or-later-licensed and free; nothing on this page is a hosted service we sell you.

  • What does self-hosting cost compared to Datadog?

    1024 MB of RAM and 5 GB of disk — the smallest tier most VPS hosts sell, about $5 a month. Netdata itself is free and GPL-3.0-or-later-licensed; the bill is the server, plus a domain you probably already own. What you stop paying: Datadog Pro, $15/mo — $180 a year.

  • How hard is it really?

    ONE COMMAND — under 10 minutes. The rule that produced that verdict: one container, no database, no outside integration, at most one secret. Nothing to negotiate with anyone else, nothing to back up separately, at most one secret to generate. This is the case where the compose file honestly is the whole install. The tier is derived from seven countable facts about the Netdata install, not from anyone's impression of it, and the whole rubric is published on the methodology page.

  • Can I run Netdata on my own computer instead of a server?

    Yes — that is the second path in the prompt box above. "On my computer" installs the same Netdata on the machine you are sitting at: no VPS, no domain, no DNS, and nothing exposed to the internet. It checks for Docker first and installs Docker Desktop if the machine does not have it — macOS, Windows and Linux each get their own step — then binds everything to loopback, so the app answers on http://localhost and only on that computer. The catch: Metrics are about this machine only, and on macOS or Windows Docker Desktop the charts largely reflect the VM. A fleet still needs one agent per host or another product. Same discipline as the cloud path: pinned images, secrets generated on the machine, and a first backup taken before the prompt says it is done.

Content last checked 2026-08-07. Verdicts are derived from the published rubric on /methodology; corrections go through the issue tracker.