Drop the cpu prefix from the cgroup path vars now that memory
accounting uses them too, and extract cgroupV2Dir() for the v2 mount
lookup previously repeated in CPU, memory and the root cgroup check.
Previously the matrix had separate entries for Docker Hub and GHCR, so
every image was built twice. Each entry now pushes to both registries
from a single build. Docker Hub is skipped when DOCKERHUB_TOKEN is
unset (e.g. forks).
Also drops the explicit latest tag on the GHCR agent image so latest
comes only from latest=auto, and lowers max-parallel to 3.
The agent runs as the beszel user under ProtectSystem=strict, so package
managers that write temp, cache or log files failed with only
"exit status 1" in the log.
- Include the command's stderr in check errors.
- dnf5 (Fedora 41+): point XDG_STATE_HOME and XDG_CACHE_HOME into the
agent's data dir. They default to $HOME, which is /nonexistent for
the beszel user. With -C, dnf5 still reads the system metadata cache.
- zypper: keep the solv cache in the data dir with --solv-cache-dir,
since the agent user cannot rebuild it in /var/cache/zypp.
- pacman: read the system sync databases with pacman -Qu instead of
syncing a private copy with checkupdates. This drops the
pacman-contrib and fakeroot dependencies and pacman's download
sandbox, which fails in containers without Landlock. Like the other
managers, it never refreshes the databases itself.
- install-agent.sh, Debian unit: add PrivateTmp=yes. The script adds it
to existing units too when re-run. dnf4 (Rocky, Alma) and zypper keep
temporary files in /var/tmp, which ProtectSystem=strict makes
read-only. dnf4 hardcodes /var/tmp, so the agent cannot work around
it. apt does not need it; the Debian unit gets it so both units stay
the same.
Check /sys/class/ieee80211 before opening netlink sockets. Looking up
the
nl80211 family while cfg80211 is not loaded makes the kernel run
modprobe,
which loads the module on hosts without wireless hardware, or fails
again
on every poll when the module is unavailable.
Inside an LXC guest, lxcfs serves /proc/stat with the counters of the
host cores in the guest's cpuset, so an idle guest sharing a core with a
busy neighbor reported near-100% CPU.
When the agent detects it is running in an LXC guest (lxcfs mounted on
/proc/stat, container=lxc, or /run/systemd/container), derive CPU usage
from the guest's own cgroup instead: cpu.stat on cgroup v2, cpuacct on
v1, read at the cgroup mount root so it covers every process in the
guest. Usage is normalized by the usable cores (the smallest of the
affinity mask, cpuset and CPU quota).
Per-core usage is omitted in this mode, since the per-core /proc/stat
counters describe shared host cores and would contradict the total.
Iowait and steal are not available from cgroup accounting and report as
zero.
Hosts and Docker/Podman agents are unaffected and keep reading
/proc/stat, as does an LXC guest whose cgroup accounting is unreadable.
---------
Co-authored-by: hank <hank@henrygd.me>
Make WebSocket reconnect and SSH fallback transitions reliable across asynchronous disconnects, stale callbacks, and overlapping connections. Keep the SSH listener available while disconnected and allow a verified WebSocket connection to take precedence when it recovers.
Co-authored-by: henrygd <hank@henrygd.me>
Raises the bandwidth threshold max to 5 GB/s without making the MB/s
slider less precise. Values are still stored in MB/s, and the unit is
inferred from the stored value when the alert sheet opens.
Right after agent start the hub requests stats immediately, and the first
sample of the interval was measured from the baseline taken at startup, only
a second or so earlier. A burst of startup I/O was then stored as the rate
for the whole minute, causing large spikes in disk I/O charts.
The first sample of an interval now must span at least half the interval.
Otherwise it only stores the snapshot, and the next sample is measured from it.
The updater and on-demand requests kept separate copies of the SSH client
(sys.client and the transport's client) and synced them after each request.
That allowed a closed client to be reinstalled over a newer one and leaked
connections that were replaced without being closed.
The updater now dials, opens sessions and tears down timed-out connections
through the transport. The dial keeps the TCP keepalive and handshake
deadline, and an OnConnect callback handles the per-connection resets.
The transport is created under a lock and agentVersion is now atomic.