The docs-fact census wave re-read the live hosts and re-keyed every page it could: runtime/payload identities, ports, sizes, ledger figures, unit state, and the retirement banners that drifted as the fleet moved. The wave was interrupted mid-run, so this commit carries only what passed the integrity gates: every touched page parses (front matter or H1+status convention intact, balanced fences, no truncated tables, trailing newline), the docs_completeness and link-integrity seams are green, and bats is 384/384. Numbers that this session proved on live hosts are keyed to the measured value, not the page's earlier claim.
67 lines
4.6 KiB
Markdown
67 lines
4.6 KiB
Markdown
---
|
|
type: reference
|
|
description: "Node-Exporter exposes host metrics (CPU, RAM, disk, network) to Prometheus."
|
|
tags:
|
|
- infrastructure
|
|
timestamp: 2026-09-11
|
|
---
|
|
|
|
# Node-Exporter
|
|
|
|
Node-Exporter exposes host metrics (CPU, RAM, disk, network) to Prometheus. One instance runs per host, each in its own container.
|
|
|
|
**Hosts:** Dankclaw (unit) + Dankserver (unit) — **Docs:** https://prometheus.io/docs/guides/node-exporter
|
|
|
|
Both hosts run the same shape as a Quadlet unit: `dankclaw/observability/quadlet/node-exporter.container` and `dankserver/observability/quadlet/node-exporter.container` (the compose-only Dankserver copy died for a day and a half before anyone noticed, so it became a unit on 2026-09-16). Both report `generated` + `active` on the host and bind their own two addresses (`systemctl --user is-enabled|is-active node-exporter`, `ss -ltnp` on dankserver, 2026-10-18); `tests/observability_quadlet.bats:419` pins the Dankserver side to a unit, not compose.
|
|
|
|
## Facts
|
|
|
|
| Item | Value |
|
|
|------|-------|
|
|
| Image | `prom/node-exporter:latest` |
|
|
| Listen addresses | `127.0.0.1:9100` and `192.168.0.143:9100` (Dankclaw, `ss -ltnp` 2026-10-18; Dankserver binds `127.0.0.1:9100` + `192.168.0.235:9100`) |
|
|
| Network | `Network=host` + `--pid=host` (`dankclaw/observability/quadlet/node-exporter.container:37-38`) |
|
|
| Unit | `dankclaw/observability/quadlet/node-exporter.container` → `node-exporter.service` |
|
|
| Paths | `--path.procfs=/host/proc --path.sysfs=/host/sys --path.rootfs=/rootfs` (`node-exporter.container:39`) |
|
|
| Auth | none — it is an unauthenticated metrics endpoint by design |
|
|
|
|
## Why it is the one LAN listener
|
|
|
|
Every other observability service publishes a port on loopback. This one runs in the host network namespace instead, and netavark DNATs only *published* ports — so a bridge container cannot reach a host-netns bind at the host's LAN address, and the exporter has to be reachable at an address the consumers can actually dial:
|
|
|
|
- **Prometheus** runs in a container on `dankclaw_default` and scrapes `host.containers.internal:9100` (`shared/prometheus/prometheus.yml:37-39`) — the gateway address netavark delivers a bridge container's host-bound dial to. It does **not** scrape `192.168.0.143:9100`; that address is refused by construction from this container, the same rule that sends `frank-dragoon` and `frank-zealot` to the gateway. `node-dankserver` is a different machine, so its LAN address is used (`prometheus.yml:41-43`).
|
|
- **The homepage status timer** runs on the host and probes `127.0.0.1:9100` (`shared/homepage/services.json:220`).
|
|
|
|
The unit therefore binds exactly those two addresses with two `--web.listen-address` flags (`node-exporter.container:39`) — never the wildcard: `ss -ltnp` (2026-10-18) shows `127.0.0.1:9100` and `192.168.0.143:9100` and nothing else. The LAN bind stays in the unit because LAN machines scrape it directly and the seam test pins it, not because this Prometheus needs it. It has no authentication, so it is never vhosted and never published beyond the LAN.
|
|
|
|
## Mounts
|
|
|
|
| Container path | Host path |
|
|
|----------------|-----------|
|
|
| `/host/proc` | `/proc` (read-only) |
|
|
| `/host/sys` | `/sys` (read-only) |
|
|
| `/rootfs` | `/` (read-only) |
|
|
|
|
The mount paths and the `--path.*` flags are a pair: changing one without the other gives an exporter that starts cleanly and reports nothing.
|
|
|
|
## Prometheus Targets
|
|
|
|
| Host | Target |
|
|
|------|--------|
|
|
| Dankclaw | `host.containers.internal:9100` (same-host bridge container → the gateway, `prometheus.yml:39`; live target `http://host.containers.internal:9100/metrics` is up, `curl -s http://127.0.0.1:9090/api/v1/targets` 2026-10-18) |
|
|
| Dankserver | `192.168.0.235:9100` |
|
|
|
|
The scrape jobs live in `shared/prometheus/prometheus.yml`; see [[Prometheus]]. The `tests/observability_quadlet.bats` seam pins both halves: the unit's two `--web.listen-address` values (`:103-104`) and the `node-dankclaw` target string `"host.containers.internal:9100"` (`:108-131`), so the exporter and its only Prometheus consumer cannot drift apart.
|
|
|
|
## Gotchas
|
|
|
|
- Host networking is required (or a published port); the deploy's health wait probes `127.0.0.1:9100/metrics` (`scripts/deploy-remote.sh:536`) and the unit's `HealthCmd` the same address from inside the host namespace (`node-exporter.container:43`).
|
|
- The `/proc`, `/sys` and root mounts must be read-only.
|
|
- Each host's instance reflects only its own host; do not try to share one.
|
|
- Collectors are the image defaults — no per-collector tuning and no textfile collector for custom metrics.
|
|
|
|
## See Also
|
|
|
|
- [[Prometheus]] — the scraper
|
|
- [[Observability]] — the full stack, including what is still unconfigured
|