It was 2 AM. My HomeLab NAS was crawling, fans spinning like a turbine, and I had no idea what was eating resources. I had Grafana + Prometheus set up — or so I thought — until I found the scraper had silently died three days ago. No alerts. No data. Just a blank dashboard mocking me at 2 AM.
That night I discovered Beszel. Ten minutes later I could see exactly which Docker container was hammering the CPU. Not because Beszel is magic, but because it actually stayed running.
Quick Start: Beszel Running in 5 Minutes
Beszel uses a hub-agent architecture. The Hub is the web dashboard you interact with; lightweight Agents run on each server you want monitored. For a single machine, you run both on the same host.
Create a working directory and drop in this docker-compose.yml:
mkdir ~/beszel && cd ~/beszel
version: "3"
services:
beszel:
image: henrygd/beszel
container_name: beszel
restart: unless-stopped
ports:
- "8090:8090"
volumes:
- ./beszel-data:/beszel_data
beszel-agent:
image: henrygd/beszel-agent
container_name: beszel-agent
restart: unless-stopped
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- PORT=45876
- KEY= # Leave empty for now — fill in after Hub setup
Start only the Hub first:
docker compose up beszel -d
Open http://your-server-ip:8090 and create your admin account. Then go to Systems → Add System and enter:
- Host:
localhost(or your server IP if accessing remotely) - Port:
45876
Beszel generates a public key. Copy it, paste it into your docker-compose.yml under KEY=, then bring the agent up:
docker compose up beszel-agent -d
Refresh the dashboard. Within about 30 seconds you’ll see CPU, RAM, disk, and network in real time. That’s genuinely it.
Deep Dive: How Beszel Actually Works
Hub and Agent Architecture
The Hub runs on PocketBase — a single-binary backend with a built-in SQLite database. That’s why the Hub idles at around 12–18 MB of RAM on my setup. No separate metric stores, no Prometheus scrape intervals to tune, no Grafana plugin versions to juggle.
Agents connect back to the Hub via SSH tunnels using ed25519 keypairs. The KEY you copy during setup is the Hub’s public key — the agent uses it to authenticate and open an encrypted channel. Your metrics never travel over plain HTTP, which matters when agents span different network segments.
What Gets Monitored Without Extra Configuration
- CPU usage (per-core breakdown available in the detail view)
- Memory and swap usage
- Disk usage and I/O per mount point
- Network bandwidth per interface
- System uptime and load average
- Docker container stats — CPU and memory per container
- GPU stats for NVIDIA and AMD cards, if present
The Docker socket mount (/var/run/docker.sock:/var/run/docker.sock:ro) is what unlocks container-level stats. Remove it if you don’t need that — the agent monitors host resources just fine without it.
Advanced Usage: Multi-Server HomeLab Setup
Adding Remote Servers
Here’s where it gets genuinely useful. Four machines in my setup — two Ubuntu servers, a Raspberry Pi 5, and an older Proxmox node — all report to a single Hub. Each agent uses around 5–15 MB of RAM and barely registers on CPU. On each remote machine, you only deploy the agent. No hub, no database, no web server of any kind.
Minimal docker-compose.yml for a remote node:
version: "3"
services:
beszel-agent:
image: henrygd/beszel-agent
container_name: beszel-agent
restart: unless-stopped
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- PORT=45876
- KEY=your_hub_public_key_here
Prefer bare-metal over Docker? The agent ships as a single binary:
curl -sL https://get.beszel.dev/agent | bash
Wrap it in a systemd service so it survives reboots:
sudo tee /etc/systemd/system/beszel-agent.service <<EOF
[Unit]
Description=Beszel Agent
After=network.target
[Service]
Environment="PORT=45876"
Environment="KEY=your_hub_public_key_here"
ExecStart=/usr/local/bin/beszel-agent
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now beszel-agent
Back in the Hub, add the remote system via Systems → Add System, enter the remote IP and port 45876, and it connects within seconds.
Alerts and Webhook Notifications
Beszel supports outbound webhooks, so you can pipe alerts to Discord, Slack, or anything that accepts a POST request. Go to Settings → Notifications, add your webhook URL, then create alert rules per system.
A Discord alert for CPU spiking above 85% for five minutes takes about two minutes to set up:
- Create a Discord webhook in your channel settings
- Paste the webhook URL into Beszel’s notification settings
- Under the system’s alert rules, set CPU > 85% for 5 minutes
Per-system thresholds mean your Raspberry Pi — which legitimately runs warmer under load — can have looser CPU limits than your main server. No constant noise from a threshold tuned for different hardware.
Practical Tips from Production
Three months running this in production. Nothing has silently broken. No scrapers dying overnight, no time-series database ballooning past 20 GB, no monitoring stack that needs its own watchdog.
Things I learned the hard way:
Pin the image version for anything you rely on. :latest is fine for testing. In production, lock it:
image: henrygd/beszel:0.9.0
Back up beszel-data daily. Everything — accounts, systems, historical metrics — lives in that one directory. A simple rsync job covers it:
rsync -av ~/beszel/beszel-data/ /mnt/backup/beszel-data/
Put the Hub behind a reverse proxy. Don’t expose port 8090 directly. Caddy makes TLS trivial:
monitor.yourdomain.com {
reverse_proxy localhost:8090
}
Check that port 45876 is reachable from Hub to Agent. Got machines behind a firewall? Either open that port specifically, or run everything on a Tailscale network. That’s my setup — monitoring traffic stays on the private overlay and never touches the public internet.
Use the network bandwidth charts to catch unexpected traffic. This is the feature I reach for most. One night the chart showed a machine pushing 40 MB/s sustained at 3 AM — a misconfigured backup job was re-uploading the same dataset every hour. Beszel flagged it in 30 seconds. Without it, I’d have caught the problem on a hosting bill a month later.
Comparing Beszel to a full Grafana + Prometheus stack isn’t quite fair — they’re different tools for different problems. Grafana makes sense when you need log correlation, custom business metrics, or alerting pipelines that trigger automated remediation.
But for HomeLab server and container health at a glance, Beszel cuts roughly 80% of the operational complexity at about 5% of the resource cost. On my setup, removing the TIG stack from the monitoring host freed nearly 800 MB of RAM — which my Plex transcoder was very happy to inherit.

