Beszel
Beszel shows what your servers are doing — CPU, memory, disk, load and every container’s share of them — and alerts when a threshold is crossed. A hub keeps the history and serves the interface; an agent on each host reports to it. The agent connects out to the hub and opens no port, and it reads Docker through a socket proxy that allows nothing but listing containers. Three services: hub, local agent and socket proxy.
Last run on a host 8 Nov 2026
Pinned at 0.21.1 (released 22 Oct 2026) · the newest upstream release
In the upstream images: 0 CRITICAL · 8 HIGH with a published fix — per image
To try it: Docker, nothing else. See Try it locally.
To deploy it behind the proxy: Traefik running and a domain.
Try it locally
Section titled “Try it locally”On your own machine, with no proxy, domain or certificate. The agent needs two
values from the hub, so the hub starts first. From monitoring/beszel/:
cp .env.local.example .env.localmkdir -p volumes/local/hub-datadocker compose -f docker-compose.local.yml --env-file .env.local up -d hubOpen http://localhost:8090, create the owner account, and put two values into
.env.local: AGENT_TOKEN from Settings → Tokens & Fingerprints, and
AGENT_KEY from + Add System — the full key, including the ssh-ed25519
prefix. Then start the rest:
docker compose -f docker-compose.local.yml --env-file .env.local up -dThe local machine appears as beszel-local within seconds. Stop it again with
the same command and down.
Installation
Section titled “Installation”From monitoring/beszel/:
-
Copy the environment file and set
APP_TRAEFIK_HOSTandTZ:Terminal window cp .env.example .envmkdir -p volumes/hub-data -
List
beszel-edgeinEDGE_NETWORKSincore/traefik/.env, then render and restart Traefik — see edge networks. -
Start the hub alone:
Terminal window docker compose up -d beszel-hubdocker compose logs beszel-hub --followWait for
Server started at http://0.0.0.0:8090, then openhttps://<APP_TRAEFIK_HOST>and create the owner account. -
Copy two values from the hub into
.env, and give this host a name:Value Where Into Registration token Settings → Tokens & Fingerprints — enable it and copy AGENT_TOKENHub public key + Add System — the full key, ssh-ed25519prefix includedAGENT_KEYA stable name your choice AGENT_SYSTEM_NAMEWithout
AGENT_SYSTEM_NAMEthe agent registers under its container ID, which changes every time the container is recreated. -
Start the agent:
Terminal window docker compose up -d agentIt dials the hub and registers itself; there is no host or port to enter. From now on
docker compose up -dstarts everything together.
Verify
Section titled “Verify”docker compose ps # three services running — hub and agent have no healthcheckThe host appears in the interface with a green dot within about ten seconds, with its containers listed. Then set a threshold you know is crossed — disk usage at 1 %, for example — and wait for the alert to arrive.
Other hosts
Section titled “Other hosts”Each further host runs the standalone agent from monitoring/beszel-agent/: the
agent and its socket proxy, nothing else. It connects out to the hub over HTTPS,
so that host needs no inbound port — only a route to the hub’s address.
Last run on a host 8 Nov 2026
Pinned at 0.21.1 (released 22 Oct 2026) · the newest upstream release
In the upstream images: 0 CRITICAL · 8 HIGH with a published fix — per image
From monitoring/beszel-agent/ on that host:
-
Terminal window cp .env.example .env -
Set four values in
.env. The token and key are the same for every agent:HUB_URL=https://beszel.example.comAGENT_TOKEN=...AGENT_KEY=ssh-ed25519 AAAA...AGENT_SYSTEM_NAME=hostbThe agent refuses to start without
HUB_URL. -
Terminal window docker compose up -ddocker compose logs -fExpect
WebSocket connected. The host appears in the hub on its own.
Keep hub and agents on the same version.
Going further
Section titled “Going further”What it reports
Section titled “What it reports”| Host | CPU in total and per core, memory and swap, root filesystem usage, disk I/O, load average |
| Containers | discovery, CPU, memory and network per container |
| Not available | the host’s network interface bandwidth |
The agent runs as an ordinary container without host networking, and a
container cannot see the host’s network interfaces. The stack sets NICS=-*, so
the field stays empty rather than showing the container’s own traffic as the
host’s. Beszel’s system Bandwidth alert therefore never fires; CPU, memory,
disk, temperature and system-down alerts work. EXTRA_FILESYSTEMS adds mount
points beyond the root filesystem.
Docker access
Section titled “Docker access”The agent never holds the Docker socket. The socket proxy holds it read-only and
grants CONTAINERS and PING, nothing else; it sits on an internal network with
no route out, and the agent reaches it by service name. A compromised agent gets
that read-only list, not root on the host. The proxy answers 403 to /version
and /info, and the agent logs both at debug level and carries on.
Who can reach it
Section titled “Who can reach it”The hub ships access-vpn, with limit-standard and headers-standard. Host
metrics describe your systems in detail.
Alerts
Section titled “Alerts”Thresholds are set per system in the hub. Beszel sends through Shoutrrr URLs — ntfy, Gotify, Matrix, webhooks — and not by email. Disk usage is the threshold that warns before an incident rather than during one. System-down alerts only work while the hub runs; a hub that stops reports nothing, its own stop included. A dead-man’s switch covers that.
Backup
Section titled “Backup”| State | volumes/hub-data — the SQLite metric store and the hub’s SSH private key |
| Critical | The key. Without it, every agent has to be registered again by hand |
| Rebuildable | the metric history |
| Quiescing | stop the hub briefly, or dump the database — the store is live |
For borgmatic, back up the whole hub-data directory —
the key is not in the database — and dump the store:
sqlite_databases: - name: beszel path: /srv/docker/monitoring/beszel/volumes/hub-data/data.dbConfirm the database filename on the running instance. Restore order: the directory, then the hub; the agents reconnect without registering again.
The standalone agent holds no state. After a loss, deploy it again with the same token and key.
Updates
Section titled “Updates”-
Read the release notes.
-
Back up
volumes/hub-data. -
Raise
APP_TAGin the hub’s.envand in every agent’s.env. -
Update the hub first, then the agents — on each:
Terminal window docker compose pull && docker compose up -d -
Check that every host reconnects and reports.
Troubleshooting
Section titled “Troubleshooting”The agent does not appear in the hub. AGENT_KEY holds only the base64 part
of the key — the ssh-ed25519 prefix is required. The key can also be read on
the hub host: sudo ssh-keygen -y -f volumes/hub-data/id_ed25519.
A host shows up twice, one entry stale. It registered under its container ID
before AGENT_SYSTEM_NAME was set. Delete the stale entry.
Files in volumes/hub-data cannot be read. The hub runs as root and owns
them. Use sudo.
403 Forbidden for /version and /info in the agent log. Expected — see
Docker access.
Repository files
Section titled “Repository files”monitoring/beszel/README.md— architecture, setup, security modelmonitoring/beszel/.env.examplemonitoring/beszel-agent/README.md— the agent for further hostsmonitoring/beszel/UPSTREAM.md— upgrade checklist
Official sources
Section titled “Official sources”© 2026 SecDockBlueCode Apache-2.0Site content CC BY-NC 4.0