Why Beszel hits the right balance for self-hosters
Grafana + Prometheus is the industry standard, but it comes with significant setup overhead: exporters, Prometheus config, Grafana datasources, dashboards. Uptime Kuma is great for availability checks but does not give you resource metrics. Netdata is data-rich but consumes more RAM per node and requires a cloud account to aggregate multiple hosts. Beszel lands in the middle: full resource metrics (CPU, RAM, disk, bandwidth, temperatures), per-container Docker stats, historical charts and threshold alerts — deployed in a single Docker command with zero config files.
The hub is built on PocketBase, which means it is a single Go binary with an embedded database. No separate Postgres or Redis. No YAML to write. The trade-off is that Beszel does not do custom queries or arbitrary dashboards like Prometheus/Grafana — for resource monitoring across a fleet, it is faster to get started and easier to maintain.
Uptime Kuma · Beszel · Netdata — comparison
| Criterion | Uptime Kuma | Beszel | Netdata |
|---|---|---|---|
| Resource metrics | No (HTTP/TCP probes only) | Yes (CPU, RAM, disk, bandwidth, temp.) | Yes (very detailed) |
| Multi-server without cloud account | Yes (one instance per server) | Yes (one hub, any number of agents) | Partial (cloud room needed to aggregate) |
| Hub RAM usage | < 100 MB | < 50 MB | 300–500 MB per node |
| Per-container Docker stats | No | Yes (auto-discovered) | Yes |
| Config required | Web UI | Zero files | Zero files |
| Prometheus export | No (native) | Not native (experimental /metrics) | Yes (native) |
What you get out of the box
- CPU, RAM, disk, bandwidth and temperature metrics updated every 15 seconds.
- Per-container Docker stats — CPU, RAM and network per container, auto-discovered.
- Historical charts at hourly, daily and monthly resolution with no retention cap.
- Threshold alerts sent to Telegram, Slack, email, Discord, ntfy, PagerDuty and more.
- Multi-server: one hub, any number of agents — no per-server fee or paywalled features.
- Agents connect out to the hub — no inbound firewall rules needed on monitored servers.
- MIT license — fully open-source, self-hosted, no cloud dependency.
Requirements
The hub is exceptionally lightweight. It runs comfortably on a 512 MB / 1 vCPU VPS — in fact, it fits alongside other services on your smallest server. Plan for 5–10 GB of disk for the PocketBase data directory, which grows slowly as historical metrics accumulate. Agents are even lighter: a few MB RAM each. You can monitor 20+ servers without the hub breaking a sweat on a 1 GB VPS.
Dependencies: Docker installed on the hub (for the hub container) and on each monitored server (for the agent in container mode). A binary alternative without Docker exists for agents — refer to the official documentation for this variant.
Deploy the hub and add multiple agents
Start the hub
On the VPS dedicated to monitoring:
docker run -d --restart=always \
-p 8090:8090 \
--name beszel \
-v /opt/beszel:/beszel_data \
henrygd/beszel:latestThe hub starts immediately — no environment variables or config files required.
Create the administrator account
Open http://<hub-ip>:8090. The first visitor sees the PocketBase wizard that creates the admin account (email + strong password). This account controls the entire dashboard — choose a solid password, you will enable 2FA in the next step.
Enable 2FA on the administrator account
In PocketBase, go to Settings → Admins, open your profile and enable two-factor authentication (TOTP). Scan the QR code with an authenticator app (Aegis, Authy, 1Password). From that point on, every login requires the time-based code in addition to the password.
Put the hub behind an HTTPS reverse proxy
Point a subdomain (e.g. monitor.yourdomain.com) to the hub VPS. With Caddy:
apt install -y caddyContent of /etc/caddy/Caddyfile:
monitor.yourdomain.com {
reverse_proxy localhost:8090
}Caddy provisions the TLS certificate automatically. Then close port 8090 in your firewall — all access goes through 443:
ufw delete allow 8090
ufw allow 443Add each monitored server
In Beszel, click Add system, enter a name and the server's IP or hostname (port 45876). The hub generates a per-system public key — copy it.
Install the agent on each monitored server
On each server to monitor:
docker run -d --restart=always \
--network host \
--name beszel-agent \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-e KEY="<key-copied-from-hub>" \
henrygd/beszel-agent:latestThe agent establishes an outbound connection to the hub on port 45876 — no inbound port to open on the monitored server. Metrics appear in the dashboard within seconds.
Configure Telegram and email alerts
In Beszel settings, open the Notifications tab and click Add notification.
Telegram (recommended for speed): create a bot via @BotFather to get a token, then retrieve your chat_id by sending a message to the bot and calling https://api.telegram.org/bot<token>/getUpdates. Paste the token and chat ID into the form. A test message confirms the connection.
Email (SMTP): fill in the SMTP host, port (587 for STARTTLS), username, password and destination address. On each monitored system, define alert thresholds: for example, alert when disk usage exceeds 80%, or when RAM stays above 90% for 5 minutes. Multiple channels can coexist — Telegram for urgent alerts, email for summaries.
Troubleshooting — silent agent, TLS rejected, port blocked
The agent does not appear in the dashboard. First check that the agent is actually running: docker logs beszel-agent. A connection refused message means port 45876 is blocked on the hub — open it on the hub side only (agents themselves do not need any inbound port):
ufw allow 45876/tcpIf the logs show a TLS error like certificate signed by unknown authority, the hub is exposed with a self-signed or unrecognized certificate. Two options: use a valid Let's Encrypt certificate (recommended path via Caddy), or pass AGENT_SKIP_TLS_VERIFY=true to the agent (avoid in production).
Hub is behind a reverse proxy but agents cannot connect. Port 45876 is a direct TCP port (not HTTP): Caddy or nginx does not proxy it the same way as a web request. The solution is to expose port 45876 directly on the hub (not via the proxy) and only put the web interface (8090) behind the HTTPS proxy. Allow port 45876 in your firewall and make sure your VPS security group allows it as TCP inbound from your agents' IPs.
Docker metrics are missing. The agent must have access to the Docker socket. Check that /var/run/docker.sock is mounted read-only in the agent start command.
Securing the hub
A monitoring hub aggregates sensitive data (load, disk, internal network topology). A few essential measures:
Restricted dashboard access. After enabling 2FA, add an IP restriction or HTTP basic auth at the reverse proxy level if the hub is only accessible to a small team. With Caddy:
monitor.yourdomain.com {
basicauth {
<user> <bcrypt-hash>
}
reverse_proxy localhost:8090
}Port 45876 not exposed publicly. If all your agents are on VPS instances sharing a private network, configure the hub to listen on the private IP only and block 45876 on the public IP. Otherwise, restrict this port to your agents' known IPs in ufw.
Back up the data directory. The /opt/beszel folder contains the PocketBase database and metrics history. Include it in your backup routine — recovery after a failure is immediate if the backup is current.
Export to Prometheus — going further
If you already have a Prometheus/Grafana stack and want to centralise all your metrics, Beszel exposes an experimental /metrics endpoint in Prometheus format on the hub. Enable it in advanced settings and add a scrape job to your prometheus.yml:
scrape_configs:
- job_name: beszel
static_configs:
- targets: ['monitor.yourdomain.com']
scheme: https
metrics_path: /metrics
basic_auth:
username: '<user>'
password: '<password>'This is not Beszel's primary path — if Prometheus/Grafana is already in place you may not need Beszel at all. But for a team that wants both dashboards, it is a useful bridge.
Run the Beszel hub on a separate VPS from your main stack. When your production server is down, you want the monitoring system to still be up and sending alerts — not down alongside it. The hub's tiny resource footprint means you can justify a dedicated 512 MB VPS for monitoring without feeling like you are wasting money.
Official documentation
For advanced configuration and tool-specific options, refer to the official Beszel documentation. This guide covers VPS deployment and common issues; the vendor documentation remains the reference for major upgrades and fine-tuning.