Why this matters beyond the homelab: Reachability and resource pressure are different questions. A service can pass every health check while the host under it runs out of memory, fills a disk, or sits hot. The fleet already had a good answer to the first question in Uptime Kuma and a heavier answer to the second in the TIG stack. What it lacked was a quick per-host view that showed something useful the moment an agent was installed, without building a dashboard from queries first.
What it is
Beszel is a lightweight monitoring hub with a small agent on each host. The hub collects CPU, memory, disk, temperatures, network interfaces, and Docker container stats, and keeps history per system. Setup is closer to “add a system and paste a key” than to writing panels.
How it’s deployed
The hub runs as a Portainer stack on Tantive-III, listening on port 8090 behind Nginx Proxy Manager at beszel.tima.dev. The proxy host needs WebSocket support turned on, or the live view degrades. Login goes through Authentik over OIDC, so Beszel joins the same SSO as the rest of the fleet instead of keeping its own accounts.
The agents are bare binaries, not containers, installed directly on the Proxmox hosts as systemd services on port 45876 with auto-update enabled. For a monitoring agent that is the right trade: it needs the real view of the hardware, and a container would only see a slice of it.
The install-script bug
On the first three nodes, the install script wrote the systemd service file but left out the hub URL and the token. An agent that was never told where the hub is has nothing to report to, so the fix was patching both values into the unit with sed on each node and restarting the service.
When the fourth node joined the cluster, I checked for the same problem deliberately and it did not recur. It’s a small bug, but it’s the same lesson monitoring exists to teach: a service that is installed and a service that is doing its job are different states, and the first one is easy to mistake for the second.
What it covers, and what it doesn’t
Three tools now answer three questions. Uptime Kuma answers “does it respond.” Beszel answers “how is this host doing right now.” Grafana on top of the TIG stack answers “what happened over time, and should it alert.” Beszel does not replace Grafana. Alerting still routes through Grafana to Discord, and Beszel’s own notifications are deferred to a larger notification project rather than bolted on here.
One placement tradeoff is worth stating plainly. The hub lives on Tantive-III, so when that VM goes down for a GPU or kernel update, the hub goes with it. Uptime Kuma runs on a different node and keeps watching, which is exactly why the two tools shouldn’t share a failure domain.
The lesson
Add a monitoring tool when it answers a question the others can’t, and hold it to the same rules as any other service: SSO, a proxy host, and a check that it is actually collecting. The collection check mattered more than the tool did.
Related: Building a TIG Stack for Homelab Observability: Telegraf, InfluxDB, Grafana | Uptime Kuma: Monitoring 25+ Services With Discord Webhook Alerts | Deploying Authentik SSO Across 15+ Services: The Full Integration Playbook