# Health Monitor

> Enroll servers with one command, watch live metrics and the 0–1000 health score, run benchmarks, and manage the xdpd fleet daemon from the console.
>
> Canonical: https://xdp.network/docs/servers/health-monitor · Updated 2026-08-17

Health Monitor is the fleet console: a small open-source daemon (`xdpd`) on your
server reports facts, live samples, VMs and events every 15 seconds, and the
dashboard turns that into a health score, charts, one-click product installs and
benchmarks. Servers connect through "Server OAuth" — no API keys to paste.

## Enroll a server

Open the **dashboard overview** and choose **Connect a service** → **Your server
(Server OAuth)** (or **Add a server (Server OAuth)** on the Servers card). The modal
mints a one-time enroll token and shows the install command:

```bash title="Run on your server as root"
curl -fsSL https://xdp.network/api/fleet/agent.sh | bash -s -- <enroll_token>
```

The daemon installs as a systemd service and checks in within seconds — the modal
polls until the new server appears, and from then on it reports every 15 seconds.
Any signed-in account can enroll a server; you do not need to own a product first.
Enrolled servers appear on the overview and on the **Health Monitor** product page,
which lists every server with its status and score ring.

:::warning
The token in the command is single-use and expires after a limited window (the modal
shows the remaining time). It is never shown again, and each server needs its own
command.
:::

## What the daemon reports

Every heartbeat (15s) sends one bundle:

- **Facts** — hostname, OS, kernel, architecture, CPU model and cores, total memory,
  virtualization kind (and whether the host can create VMs), cloud provider, uptime,
  public IP and the daemon's own version.
- **A sample** — CPU %, load averages, memory (with a used/cached/buffers/free
  breakdown), swap, per-disk usage with inode % and read/write throughput, per-NIC
  throughput plus error/drop counters, TCP connections, process count, CPU
  temperature, per-core busy % and clock speed, and the top processes by CPU and RSS.
- **VMs** — qemu/lxc/kvm guests discovered on the host.
- **Events** — OOM kills, failed services, reboots, disk pressure, cgroup CPU
  throttling, zombie process hordes and daemon lifecycle notes.

Samples are kept as 1-minute rows for 7 days and as hourly rollups for 90 days;
events are kept for 30 days.

## The health score

Each server gets a score from 0 to 1000, recomputed server-side from the recent
sample window plus the last 24 hours of events. Instead of judging one instant, the
score measures **sustained** stress: each component is the p80 of roughly the last 20
minutes of samples, so a one-sample spike barely moves the score while a 20-minute
grind does.

| Component | Weight | What drags it down |
| --- | --- | --- |
| CPU | 20% | Sustained high CPU %, high load-per-core, sudden spikes |
| Memory | 20% | Sustained high memory use, swap pressure |
| Disk | 20% | A mount filling up, inode exhaustion |
| Network | 10% | NIC error/drop counters rising over the window |
| Stability | 20% | Critical and warning events in the last 24h |
| Freshness | 10% | Late or missing heartbeats |

A trend modifier compares the recent half of the window with the previous half:
rising stress subtracts a small penalty, easing stress adds a small bonus. A server
with no heartbeat for over two minutes is **offline** and its score is capped at 50.
Scores read as *healthy* (800+), *strained* (500–799) and *critical* (below 500).

The header card on the server page shows the score ring, one mini-bar per component
(hover for its weight) and a **Why this score** list naming the exact pressures —
for example "sustained CPU 92% for ~20m" or "2 OOM kills in 24h".

## The server page

Click a server to open its page (`/dashboard/fleet/<id>`). The page polls every 15
seconds, so what you see tracks the daemon.

- **Overview** — the host-facts card (OS, kernel, CPU, virtualization, uptime, daemon
  version, and the update button covered below), a live-sample card with usage bars
  and NIC error/drop badges, a segmented memory-breakdown bar, a per-core CPU heat
  strip (one cell per logical core — hover for the exact busy %, clock speed and
  socket), and a top-processes table.
- **Products** — every product attached to the server (daemon, firewall installs,
  tunnel endpoints, optimizer runs, benchmarks) with state and a Manage link, plus
  one-click installs for the server-eligible products on your account. Installs queue
  as fleet jobs and start on the next heartbeat; OpenShield-XDP installs through its
  own licensed wizard instead. The **Server actions** card queues daemon-level
  operations: **Restart daemon** (reporting resumes within ~30s), **Re-enroll**
  (mints a fresh single-use enroll command), and **Run command** (an arbitrary script
  as root — staff accounts only, audit-logged). Offline servers run queued actions
  when they check back in.
- **Resources** — time-series charts over 1h/24h/7d: CPU, per-core CPU (average and
  busiest), memory, memory available, swap, disk, disk I/O, load, processes and
  network in/out, each with avg · p95 · peak baselines, plus an hourly bandwidth
  **usage card**.
- **Timeline** — one chronological feed of fleet events, attack transitions, jobs,
  OpenShield installs, and optimizer and benchmark runs.
- **VMs** — the guests discovered on the host (name, kind, status, CPU, memory,
  uptime). Enroll a hypervisor (Proxmox, KVM, VMware) and its guests appear
  automatically.
- **Events** — the server's raw event timeline with severity and kind badges, newest
  first, kept for 30 days.

## Benchmarks

The **Benchmark** tab runs a one-shot hardware benchmark, measured live on the box —
never simulated. Click **Run benchmark** (or the compact Run button in the header
chip); the daemon picks the job up on its next heartbeat and the tab streams a live
console. One run can be active per server, with a cap of 10 runs per server per day.

The agent measures CPU (single- and multi-thread), disk (4k random and sequential
read/write) and network (download bandwidth against multiple endpoints). Each section
is scored 0–100 against modern reference hardware and weighted into the total: **CPU
30%, disk 30%, network 25%, stability 15%** (stability is multi-core scaling
efficiency). A section that cannot run reports nulls and its weight redistributes.

Finished runs carry a hardware **fingerprint** (a shortened sha256 of the machine
identity) and a **verified** badge when the built-in consistency checks pass —
tamper-evidence, not proof. You can **share** a run with a read-only public link —
temporary (expires after 7 days) or permanent, revocable at any time — and
**compare** two of your runs, or one run against a pasted share link, with
per-section deltas.

## Update the daemon

The daemon reports its version with every heartbeat, and the Overview tab compares it
against the latest build: when they match you see a **daemon up to date** badge, and
when they don't an **Update daemon** button appears, showing the version it will
update to. Clicking it queues a self-update — the server pulls the latest build and
restarts within about a minute. Daemons too old to report a version count as
outdated.

## Remove a server

Open the server card's menu on the Health Monitor page and choose **Remove server**.
You get two modes:

- **Graceful (default)** — an uninstall job is queued; the daemon wipes its own
  service, binary, config and logs on its next heartbeat, and the row disappears when
  it acknowledges.
- **Force delete immediately** — skips the self-uninstall and deletes the row at
  once; the daemon's token simply stops working. Use this when the server is already
  gone or has been reinstalled.

## Next steps

- [Alerts](/docs/servers/alerts) — get notified when a server goes offline, drops below a score, or comes under attack
- [Server shows offline](/docs/troubleshooting/common-issues)
- [REST API v1](/docs/reference/api) — servers and benchmarks over the public API
- [Dashboard overview](/docs/getting-started/what-is-xdp-network)
