Beszel: Lightweight Self-Hosted Server Monitoring
What Beszel is
Beszel is a self-hosted server monitoring platform written in Go. The README describes it as "a lightweight server monitoring platform that includes Docker statistics, historical data, and alert functions", with a web interface, simple configuration, automatic backups, multi-user support, OAuth authentication and API access. The pitch is narrow on purpose: you want CPU, memory, disk, network, temperature and container usage for a handful of machines, a chart history to look back at, and an alert when a disk fills up or a host goes quiet, without first building a metrics pipeline.
The audience is clear from what the project measures. Homelab operators, people running a few VPS instances, and small teams with a rack of Docker hosts get the most out of it: each host runs a small agent, and one hub shows every system on a single dashboard. The README's first feature bullet sets the tone, claiming Beszel is "smaller and less resource-intensive than leading solutions". That is the project's own claim, not an independent measurement, but the single-binary design and the numbers from the test run below suggest it is not an empty one.
The repository was created on 7 July 2024 and is MIT-licensed. Over that time it has picked up network monitoring, S.M.A.R.T. disk health, ZFS pools, systemd services and GPU metrics, so it covers more than the "Docker stats dashboard" label it sometimes gets.
How it works: hub, agent and the connection model
Two binaries
The README's architecture section names two components. The hub is "a web application built on PocketBase that provides a dashboard for viewing and managing connected systems". The agent "runs on each system you want to monitor and communicates system metrics to the hub". The repository's go.mod pins PocketBase v0.42.2 and targets Go 1.27.3. Because the hub is a PocketBase application, it inherits PocketBase's pieces: an SQLite-backed data directory (beszel_data by default), a REST API under /api/, a superuser admin dashboard at /_/, and a CLI built with Cobra that adds Beszel's own update and health commands.
Both binaries are built with CGO_ENABLED=0, and the Dockerfiles copy the result into a scratch image together with only the CA certificate bundle. The hub image exposes port 8090, declares /beszel_data as a volume and runs serve --http=0.0.0.0:8090; the agent image declares /var/lib/beszel-agent as its data volume.
SSH keys, tokens and WebSockets
Authentication between hub and agent is built around an SSH key pair. On first start the hub generates an id_ed25519 key in its data directory (confirmed in the hands-on run below). The agent refuses to start without a public key: its source returns the error "no key provided: must set -key flag, KEY env var, or KEY_FILE env var". The agent embeds an SSH server and keeps a list of accepted SSH public keys, so the hub can pull metrics from it over SSH on the listen port, which defaults to 45876 in the install scripts and the agent compose file.
Newer agents can also dial out to the hub. The agent accepts a hub URL (-u/--url or HUB_URL) and a token (-t/--token or TOKEN), and the changelog describes a WebSocket connection path with SSH as the fallback. Version 0.12.10 added a display of the connection type (WebSocket / SSH) in the hub UI; 0.21.0 improved "WebSocket and SSH fallback handling" and added a DISABLE_SSH option on the hub to skip the SSH fallback entirely. That matters in networks where the hub cannot reach agents directly, for example agents behind NAT: the agent connects out, and no inbound port has to be opened on the monitored host.
Tokens come in two forms in the changelog. Agent tokens are masked in the UI until explicitly revealed (0.21.1). A "universal token" lets new agents register themselves; 0.18.0 added an option to make it permanent, 0.13.0 added SYSTEM_NAME to override the name an agent registers with, and 0.18.7 restricted the universal token API to non-superuser accounts. Each agent also keeps a fingerprint in its data directory, which the beszel-agent fingerprint command can view or reset; the reset message says a new one "will be generated on next start".
Where data lives
Everything the hub knows sits in the PocketBase data directory. The README lists automatic backups to disk or S3-compatible storage. Infrequently changing host information is stored separately in a system_details collection (added in 0.18.0), and S.M.A.R.T. data is collected in the background every hour by default, adjustable with SMART_INTERVAL.
What it monitors
The README's feature list and "Supported metrics" section, plus the changelog, give a concrete picture:
- Host basics. CPU usage, memory (including swap and ZFS ARC), disk usage and disk I/O across multiple partitions and devices, network usage and load average.
- Containers. Status and metrics of running Docker and Podman containers, with CPU, memory and network history per container. The agent reads the Docker socket, which the compose file mounts read-only. Later releases added container logs, an "image update available" flag (0.20.0) and
CONTAINER_NAME_LABELto name containers from a label (0.21.1). - Sensors. Temperatures from host sensors, fan speed on Linux via
/sys/class/hwmon, and battery charge. 0.21.2 addedFAN_SENSORSto include or exclude fan sensors. - GPUs. Usage and power draw for Nvidia, AMD and Intel. The agent compose file notes that specialised images exist for Nvidia and Intel GPUs, and 0.21.2 added video encode and decode engine usage when
nvtopis used. - Disk health. S.M.A.R.T. data, including eMMC wear and Linux mdraid array health via sysfs, with notifications on drive failure and a dedicated S.M.A.R.T. page (0.17.0).
- ZFS and btrfs. ZFS pool capacity, health, I/O throughput, scrub status and per-dataset usage; btrfs filesystems are reported as storage pools since 0.20.0.
- systemd services. Basic service monitoring since 0.16.0, filterable with
SERVICE_PATTERNS, alerts for failed services (0.19.0) and service logs in the details view (0.21.0). - Network monitors. Since 0.20.0, agents can probe targets themselves and report response time and interruptions. The changelog mentions HTTPS monitors with TLS certificate expiry checks and alerts, DNS monitors with a configurable DNS server, and comparisons across systems.
- Pending package updates. 0.21.0 added a list of pending package updates on the system page.
- Alerts. Configurable alerts for most metrics, low-battery alerts, GPU usage alerts, quiet hours to silence alerts during set periods (0.17.0) and styled HTML alert emails (0.21.2). The changelog references the
shoutrrrnotification library, which 0.12.10 says gained Signal and WeChat Work support. - Users and auth. Multi-user, where "users manage their own systems" and admins can share systems across users; OAuth2 / OIDC providers, with password auth optionally disabled; email-based one-time passwords via
MFA_OTP; and aTRUSTED_AUTH_HEADERfor forwarded authentication, restricted by aTRUSTED_PROXY_IPSallowlist since 0.20.0.
Getting started
The full quick start lives on beszel.dev. The repository ships compose files under supplemental/docker/ that show the shape of a deployment. The hub on its own:
services:
beszel:
image: "henrygd/beszel"
container_name: "beszel"
restart: unless-stopped
ports:
- "8090:8090"
volumes:
- ./beszel_data:/beszel_data
# healthcheck:
# test: ['CMD', '/beszel', 'health', '--url', 'http://localhost:8090']
# interval: 120s
# start_period: 10s
# timeout: 5s
An agent on each monitored host. Host networking lets it see real interfaces, and the Docker socket is mounted read-only for container stats. The placeholders are filled from the hub UI when a system is added:
services:
beszel-agent:
image: henrygd/beszel-agent
container_name: beszel-agent
restart: unless-stopped
network_mode: host
volumes:
- ./beszel_agent_data:/var/lib/beszel-agent
- /var/run/docker.sock:/var/run/docker.sock:ro
# monitor other disks / partitions by mounting a folder in /extra-filesystems
# - /mnt/disk1/.beszel:/extra-filesystems/disk1:ro
environment:
LISTEN: 45876
KEY: "<public key>"
HUB_URL: "<hub url>"
TOKEN: "<token>"
A third compose file, same-system/docker-compose.yml, runs hub and agent on one host and connects them through a Unix socket in a shared volume instead of a TCP port, using LISTEN: /beszel_socket/beszel.sock on the agent. Anyone already using Docker Compose or Podman can adapt these files directly.
Without containers, the systemd guide in the repository provides install scripts for both binaries. For the hub:
curl -sL https://raw.githubusercontent.com/henrygd/beszel/main/supplemental/scripts/install-hub.sh -o install-hub.sh && chmod +x install-hub.sh
./install-hub.sh
And for the agent:
curl -sL https://raw.githubusercontent.com/henrygd/beszel/main/supplemental/scripts/install-agent.sh -o install-agent.sh && chmod +x install-agent.sh
./install-agent.sh
The guide says the script creates a dedicated beszel user, downloads the latest release and installs the service. The agent script's help text lists -k for the SSH key ("required, or interactive if not provided"), -p for the port, -t for a token, --auto-update to control automatic daily updates and --mirror to use a GitHub proxy (default https://gh.beszel.dev) where GitHub is hard to reach. The hub script also handles FreeBSD through an rc service, and a separate Homebrew script exists for macOS agents. Updating a script-installed hub is a single command:
sudo /opt/beszel/beszel update && sudo systemctl restart beszel-hub
Hands-on: what was seen running v0.21.2
In a test run on 14 November 2026 on a 2-vCPU Linux x86_64 VM, the v0.21.2 release assets (published 12 November 2026) were downloaded from GitHub releases and started without Docker. This was a short smoke test of the hub binary, not a load test.
| Check | Result |
|---|---|
| Release tarballs | beszel_linux_amd64.tar.gz 13.4 MB, beszel-agent_linux_amd64.tar.gz 4.5 MB; each also contains LICENSE, readme.md and beszel-agent-launcher.sh |
| Extracted binaries | beszel 34,250,912 bytes (about 34 MB); beszel-agent 10,768,544 bytes (about 10.8 MB) |
| Version output | beszel version 0.21.2 and beszel-agent 0.21.2 |
| Hub subcommands | health, migrate, serve, superuser, update |
| Notable hub flags | --dir (data directory, default beszel_data), --encryptionEnv (an env var whose 32-character value encrypts app settings; unset by default), --queryTimeout (default 30s), --dev |
| Agent subcommands and flags | fingerprint, health, update; flags -k/--key, -l/--listen, -t/--token, -u/--url, -c/--china-mirrors |
| First start log | Server started at http://127.0.0.1:8090, then the PocketBase admin dashboard at /_/ and the REST API at /api/ |
GET /api/health | HTTP 200, {"message":"API is healthy.","code":200,"data":{}} |
Files in beszel_data/ | data.db, auxiliary.db (with SQLite -shm/-wal files) and id_ed25519, the hub's SSH key pair generated on first start; 944 KB in total |
| Idle memory | About 34 MB resident (RSS) with no agents connected, from a single measurement, not a benchmark |
Two practical notes came out of the run. First, beszel health with no arguments only printed the usage text. The hub source marks --url as a required flag, and the health check simply requests <url>/api/health with a three-second timeout and expects a 200, so a container healthcheck has to pass the base URL, as the commented-out block in the compose file does. Second, the id_ed25519 file is the identity agents trust. Back it up with the rest of beszel_data: a hub rebuilt with a fresh data directory generates a new key, and agents configured with the old public key will no longer accept it.
Configuration knobs worth knowing
Most behaviour is controlled through environment variables documented on the project's environment variables page. The ones that show up in the changelog and compose files are a useful shortlist:
KEY/KEY_FILE,TOKEN/TOKEN_FILE,HUB_URL,LISTENon the agent, which map to the CLI flags above.CA_CERT_FILEon the agent. Since 0.19.0 agents verify HTTPS certificates, and the changelog flags this as a potential breaking change: a hub behind a self-signed certificate needs this set, "or the connection will be rejected".HUB_HEADERSon the agent (0.21.1) to add custom headers to the WebSocket connection, handy behind an authenticating proxy.DISABLE_SSH, added in 0.18.4 "to disable SSH agent functionality" and extended in 0.21.0 as a hub option to skip the SSH fallback.APP_URLon the hub, set in the same-system compose file.SKIP_GPU,SKIP_SYSTEMD,SERVICE_PATTERNS,FAN_SENSORS,DISK_USAGE_CACHE,SMART_INTERVALandEXTRA_FILESYSTEMSto tune what the agent collects. A comment in the agent source says disk usage is cached "to avoid waking sleeping disks", which is whatDISK_USAGE_CACHEtunes.SYNC_SYSTEM_NAMES(0.21.0) to keep system names in step with hostnames.MFA_OTP,OAUTH_DEFAULT_USER_ROLE,OAUTH_DISABLE_POPUP,TRUSTED_AUTH_HEADERandTRUSTED_PROXY_IPSfor authentication.
For packagers, 0.21.2 added a no_self_update build tag that disables hub and agent self-updates. The agent source hides its update command and mirror flag when that tag is used.
How Beszel compares
Beszel sits between an uptime checker and a full metrics stack. The table describes the alternatives only at the category level. Each is a different kind of tool, and several are often combined.
| Tool | Primary job | How it relates to Beszel |
|---|---|---|
| Beszel | Host and container resource monitoring with history and alerts, via a hub and per-host agents | Fixed set of system metrics, built-in dashboard and alerting, minimal setup |
| Uptime Kuma | Uptime and endpoint checks with notifications and status pages | Answers "is it reachable?" from the outside; Beszel answers "how is the host doing?" from inside. Beszel's agent-side network monitors overlap with part of this. |
| Prometheus | Metrics time-series database with pull-based scraping, a query language and alerting rules | General-purpose and extensible to any exporter or application metric; more moving parts and more configuration than Beszel's fixed metric set |
| Grafana | Dashboards and visualisation over many data sources | Not a collector by itself; usually paired with a metrics store. Beszel bundles collection, storage and UI in one product. |
| VictoriaMetrics | Time-series database, commonly used as Prometheus-compatible long-term storage | Targets larger metric volumes and retention needs than a single SQLite-backed hub |
| SigNoz | Observability platform covering metrics, logs and traces, built around OpenTelemetry | Aimed at application observability; Beszel stays at the infrastructure level |
A rough rule: if the question is "which of my twelve boxes is running out of disk, and which container is eating RAM?", Beszel is the shortest path. If the question involves application latency, custom business metrics or ad-hoc queries over months of data, a Prometheus-style stack with Grafana on top is the usual answer.
When not to use Beszel: limits and trade-offs
- Fixed metric set, no query language. Beszel collects what its agent knows how to collect. Nothing in the README or changelog describes scraping arbitrary exporters, custom application metrics or a query language. If you need those, Beszel will not grow into them.
- Not a log or tracing platform. The changelog shows container logs and systemd service logs in the UI, but nothing describes log aggregation, search or distributed tracing.
- Single hub on SQLite. The hub is one PocketBase process with SQLite files in a data directory. That keeps operations simple, but the sources read describe no clustering or high-availability mode. Retention and downsampling behaviour is not documented in the README, so check the website docs before planning for long histories.
- Key and token hygiene is on you. The hub's
id_ed25519key, agent tokens and the universal token are the trust anchors. A permanent universal token lets any agent holding it register, which is convenient for fleets and risky if it leaks. Agents also listen on a port (45876 by default) when SSH is enabled, so firewall that port to the hub, or use the outbound WebSocket mode withDISABLE_SSH. - Agents need privileged views of the host. Container metrics require the Docker socket, which the compose file mounts read-only. Host networking is used so interface stats are real. That is normal for a monitoring agent, but read access to the Docker socket still exposes a lot of information.
- The admin surface is PocketBase's. The test run showed the PocketBase superuser dashboard at
/_/next to the Beszel UI. Put the hub behind TLS and a reverse proxy before exposing it. 0.21.2 added a rate limit of 10 sign-in attempts per minute per address, plus warnings for missing trusted proxy configuration. - The permission model is still being hardened. The 0.21.2 changelog fixes agents being able to overwrite another system's container, service, S.M.A.R.T. or ZFS records, non-admin users being able to change system users, and missing permission checks when moving network monitors. If you run a shared, multi-tenant hub, track releases closely.
- Self-signed TLS needs extra setup. Since 0.19.0, agents reject untrusted certificates unless
CA_CERT_FILEis set. - Platform split. The release config builds the hub for Linux, macOS, Windows and FreeBSD, and the agent for those plus OpenBSD, across architectures from amd64 and arm64 to mips, riscv64 and ppc64le. Some metrics are platform-specific (fan speed is listed as Linux-only via hwmon), and the changelog has a steady stream of macOS, Windows and FreeBSD fixes.
- Single maintainer, limited triage. The README says the maintainer tries to respond to issues "but may not always have time to do so".
SECURITY.mdasks that security advisories be used only for "real high severity vulnerabilities", citing a flood of erroneous AI-generated reports. - Integration API churn. 0.21.1 made
Agent.Startrequire acontext.Contextfor anyone embedding the agent as a Go package, and deprecated the GB-based memory fields in favour of raw byte values that will be removed in a future version.
What changed in v0.21.x
The 0.21 line, which started with 0.21.0, added pending package updates, Wi-Fi signal strength, TLS certificate expiry checks for HTTPS monitors, systemd service logs and network monitor comparisons. 0.21.1 added opt-in cgroup CPU and memory accounting, a TLS expiry alert, pagination in data tables, IRQ/SoftIRQ/Nice in the CPU breakdown and env/envFrom options in the Helm chart. That last item confirms a Helm chart exists for Kubernetes deployments.
v0.21.2, released on 12 November 2026, is mostly a hardening release: the permission fixes listed above, a fix for hub crashes caused by null entries in agent data, a fix for agent health checks reporting disconnected agents as healthy, fixes for hub updates failing because of database access, working directory or SELinux context issues, NVMe S.M.A.R.T. counters larger than 64 bits, and a fix for the agent's -k flag not accepting a key file path. The test run's agent help text matches that last fix: -k is described as taking "inline key material or path to a key file".
Project activity
Checked 14 November 2026: latest release v0.21.2 (12 November 2026), MIT licence, about 27,300 stars on the henrygd/beszel repository, last push 13 November 2026. The project is written in Go, was created on 7 July 2024 and develops on the main branch. Translations are managed through Crowdin. Bug reports and feature requests go to GitHub issues; support questions go to GitHub discussions or the community-run Matrix room #beszel:matrix.org. Documentation, including the quick start, agent and hub installation guides and the environment variable reference, is at beszel.dev.
Enjoying this project?
Discover more amazing open-source projects on TechLogHub. We curate the best developer tools and projects.
Repository:https://github.com/henrygd/beszel
GitHub - henrygd/beszel: Beszel: Lightweight Self-Hosted Server Monitoring
Beszel is an MIT-licensed Go server monitor: a PocketBase hub plus small per-host agents tracking CPU, memory, disk, network, Docker and Podman containers, GPUs...
github - henrygd/beszel
Related Projects
RAGFlow: Open-Source RAG Engine with Agentic Retrieval
Self-hosted RAG engine with deep document parsing, grounded citations and agentic retrieval.
Uptime Kuma: Self-Hosted Uptime Monitoring Tool
Self-hosted uptime monitor with 90+ notification services, status pages and a WebSocket-driven web dashboard.
Hugo: Fast Static Site Generator Written in Go
Single-binary Go static site generator with built-in asset pipelines, live dev server and Hugo Modules.
Maintain Beszel: Lightweight Self-Hosted Server Monitoring? Add the badge to your README
Show that the project is featured on TechLogHub and send readers to this write-up.
[](https://techloghub.com/open-source/beszel?utm_source=techloghub_badge&utm_medium=referral&utm_campaign=readme)