手头管理的服务器越来越多,一般不出事,偶尔磁盘不足或者什么情况,就麻烦了,原本我是用 Grafana:Prometheus + node_exporter 来监视本地的一些服务器,安装相对复杂,不要搞了半天不仅没减少工作,反倒增加了工作。调查了下,使用Beszel来监视多个服务器实例的状态。似乎相对比较简单。

Beszel 极简部署方案总结:Hub 用 Docker,Agent 用二进制

这次我最后定下来的方案是:

  • Hub:Docker / Docker Compose 部署
  • Agent:Linux 二进制 + systemd 部署
  • 通信:WebSocket only
  • Agent 统一配置:DISABLE_SSH=trueLOG_LEVEL=warn
  • 额外补一层 logrotate,防止日志文件长期变大

这样做的核心目标不是“功能最全”,而是“多台主机统一监控、部署件数少、批量装机简单、维护成本低”。Beszel 官方支持 Hub 通过 Docker/Podman 或单二进制安装;Agent 侧要求的关键变量是 KEYTOKENHUB_URL,其中 HUB_URL 明确用于 outgoing WebSocket connection

1. Hub 端:Docker 部署最省事

Beszel 官方 Hub 安装文档给出的最小 Docker Compose 结构非常直接:镜像是 henrygd/beszel,默认对外端口是 8090,持久化目录是 ./beszel_data,并建议设置 APP_URL。官方同时支持用 USER_EMAILUSER_PASSWORD 在首次启动时创建初始用户。

一个很适合博客展示的最小示例就是:

services:
  beszel:
    image: henrygd/beszel:latest
    container_name: beszel
    restart: unless-stopped
    environment:
      APP_URL: http://你的公网IP或域名:8090
      USER_EMAIL: 你的邮箱
      USER_PASSWORD: 你的密码
    ports:
      - "8090:8090"
    volumes:
      - ./beszel_data:/beszel_data

启动方式也很简单:

docker compose up -d

这里 restart: unless-stopped 的好处是,容器创建后只要宿主机的 Docker 服务正常开机启动,Hub 容器也会自动拉起。这个是 Docker 行为,不是 Beszel 特有逻辑;而 Hub 文档本身就是按 restart: unless-stopped 给示例的。

2.Agent 端:为什么改成“二进制 + systemd”

Beszel 官方 Agent 文档明确给出了三项关键变量:

  • KEY:在 Hub 里添加系统时显示的公钥
  • TOKEN:Agent 认证用 token,可从 /settings/tokens 获取
  • HUB_URL:Agent 发起 WebSocket 连接时使用的 Hub 地址。

因此,Agent 完全可以脱离容器,直接按“二进制程序 + 配置文件 + systemd 服务”这套 Linux 传统方式跑。对博客读者来说,这个方式的优点非常好解释:

  • 远端机器不用再依赖 Docker
  • 部署件数更少
  • 适合批量 SSH 推送安装
  • 开机自启和日志管理都能完全交给 systemd 和 logrotate

这个思路和 Beszel 官方“二进制安装 + service 持续运行”的设计方向是吻合的。

3.为什么一定强调 WebSocket only

Beszel 官方安全文档写得很清楚:WebSocket 连接是由 Agent 主动发起,连接到 Hub 的 URL,并且握手时会做双向校验。也就是说,只要 Agent 能主动访问 Hub,就很适合用 WebSocket 模式。

另外,官方环境变量页明确给了:

  • DISABLE_SSH=false 默认值
  • DISABLE_SSH=true 的含义是:完全关闭 SSH server(WebSocket connection only)

这也是我最后建议统一成 WebSocket-only 的原因:

  • 被监控主机不用开放 Agent 入站端口
  • 只要能从 Agent 主动访问 Hub 即可
  • 安全面更收敛
  • 云主机批量部署时更省心。

4.为什么不直接照抄 Hub 页面生成的 Agent 安装命令

Hub 页面会给出可复制的二进制安装命令,这点没问题;但如果您的目标是“只走 WebSocket,不留 SSH 回退”,那么不能只照抄默认命令就结束。官方环境变量页表明 DISABLE_SSH 默认是 false,而维护者在 2026 年的讨论里明确说过:只要 Agent 端设置 DISABLE_SSH=true,才是纯 WebSocket 模式;否则 WebSocket 失败时会看到 “Starting SSH server” 这一类回退行为。

所以我的结论是:

  • Hub 页面生成的命令可以作为参考
  • 但如果追求统一架构,最好还是自己封装一个安装脚本
  • 让脚本默认写死 DISABLE_SSH=trueLOG_LEVEL=warn 等参数。

5.这次我自定义的 Agent 安装脚本在做什么

我给自己写的不是 Beszel 官方脚本,而是一个适合批量装机的二次封装脚本。它的目标只有一个:把官方支持的 Agent 运行方式,收敛成一个适合 Ubuntu/Debian 和 CentOS/RHEL 系列的“一键安装模板”。

它做的事情主要包括:

  • 下载对应架构的 beszel-agent 二进制
  • 创建专用用户 beszel
  • 写入配置文件 agent.env
  • 注册 systemd 服务
  • 启动并设置开机自启
  • 自动补一份 logrotate 配置

这里面真正与 Beszel 官方变量对应的部分是:

  • KEY
  • TOKEN
  • HUB_URL
  • DISABLE_SSH=true
  • LOG_LEVEL=warn
  • DOCKER_HOST=""
  • SYSTEM_NAME

其中:

  • LOG_LEVEL 官方支持 debug / info / warn / error
  • DOCKER_HOST="" 官方明确表示可用于完全禁用 Docker 监控
  • SYSTEM_NAME 官方说明:如果不设置,使用主机名。

这意味着:如果您的博客定位是“只做基础主机监控”,那完全可以把脚本默认值写成:

DISABLE_SSH=true
LOG_LEVEL=warn
DOCKER_HOST=

这样既简洁,也符合“只监控系统资源、不碰容器”的取舍。

自定义的脚本文件:

Ubuntu系:

#!/usr/bin/env bash
set -Eeuo pipefail

# ========= 用户可修改变量 =========
HUB_URL="${HUB_URL:-}"
TOKEN="${TOKEN:-}"
KEY="${KEY:-}"
SYSTEM_NAME="${SYSTEM_NAME:-}"
BESZEL_VERSION="${BESZEL_VERSION:-latest}"   # 可写 latest 或具体版本,如 v0.18.6
INSTALL_DIR="${INSTALL_DIR:-/opt/beszel-agent}"
DATA_DIR="${DATA_DIR:-/var/lib/beszel-agent}"
CONFIG_DIR="${CONFIG_DIR:-/etc/beszel-agent}"
LOG_DIR="${LOG_DIR:-/var/log/beszel-agent}"
LOG_FILE="${LOG_FILE:-$LOG_DIR/agent.log}"
SERVICE_NAME="${SERVICE_NAME:-beszel-agent}"

# ========= 固定默认策略 =========
DISABLE_SSH="true"
LOG_LEVEL="warn"
DOCKER_HOST=""   # 明确禁用 docker 监控,降低权限需求和复杂度

# ========= 基础检查 =========
if [[ $EUID -ne 0 ]]; then
  echo "请用 root 运行。"
  exit 1
fi

if [[ -z "$HUB_URL" || -z "$TOKEN" || -z "$KEY" ]]; then
  echo "缺少必要变量。请至少传入:HUB_URL TOKEN KEY"
  echo '示例:'
  echo 'HUB_URL="https://hub.example.com" TOKEN="xxxxx" KEY="ssh-ed25519 AAAA..." bash install-beszel-agent-debian.sh'
  exit 1
fi

if ! command -v curl >/dev/null 2>&1; then
  apt-get update
  apt-get install -y curl ca-certificates tar logrotate
else
  apt-get update
  apt-get install -y ca-certificates tar logrotate
fi

# ========= 架构识别 =========
ARCH_RAW="$(uname -m)"
case "$ARCH_RAW" in
  x86_64|amd64) ARCH="amd64" ;;
  aarch64|arm64) ARCH="arm64" ;;
  *)
    echo "不支持的架构: $ARCH_RAW"
    exit 1
    ;;
esac

# ========= 创建用户和目录 =========
if ! id beszel >/dev/null 2>&1; then
  useradd --system --home "$DATA_DIR" --shell /usr/sbin/nologin --create-home beszel
fi

mkdir -p "$INSTALL_DIR" "$DATA_DIR" "$CONFIG_DIR" "$LOG_DIR"
chown -R beszel:beszel "$DATA_DIR" "$LOG_DIR"
chmod 750 "$DATA_DIR" "$LOG_DIR"

# ========= 下载 agent =========
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT

if [[ "$BESZEL_VERSION" == "latest" ]]; then
  DOWNLOAD_URL="https://github.com/henrygd/beszel/releases/latest/download/beszel-agent_linux_${ARCH}.tar.gz"
else
  DOWNLOAD_URL="https://github.com/henrygd/beszel/releases/download/${BESZEL_VERSION}/beszel-agent_linux_${ARCH}.tar.gz"
fi

echo "下载: $DOWNLOAD_URL"
curl -fL "$DOWNLOAD_URL" -o "$TMP_DIR/beszel-agent.tar.gz"
tar -xzf "$TMP_DIR/beszel-agent.tar.gz" -C "$TMP_DIR"

install -m 0755 "$TMP_DIR/beszel-agent" "$INSTALL_DIR/beszel-agent"
chown root:root "$INSTALL_DIR/beszel-agent"

# ========= 写环境文件 =========
cat > "$CONFIG_DIR/agent.env" <<EOF
HUB_URL=${HUB_URL}
TOKEN=${TOKEN}
KEY=${KEY}
DISABLE_SSH=${DISABLE_SSH}
LOG_LEVEL=${LOG_LEVEL}
DOCKER_HOST=${DOCKER_HOST}
DATA_DIR=${DATA_DIR}
EOF

if [[ -n "$SYSTEM_NAME" ]]; then
  echo "SYSTEM_NAME=${SYSTEM_NAME}" >> "$CONFIG_DIR/agent.env"
fi

chmod 600 "$CONFIG_DIR/agent.env"
chown root:root "$CONFIG_DIR/agent.env"

# ========= systemd service =========
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=Beszel Agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=beszel
Group=beszel
EnvironmentFile=${CONFIG_DIR}/agent.env
ExecStart=${INSTALL_DIR}/beszel-agent
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=${DATA_DIR} ${LOG_DIR}
StandardOutput=append:${LOG_FILE}
StandardError=append:${LOG_FILE}

[Install]
WantedBy=multi-user.target
EOF

# ========= logrotate =========
cat > "/etc/logrotate.d/${SERVICE_NAME}" <<EOF
${LOG_FILE} {
    daily
    rotate 7
    size 10M
    missingok
    notifempty
    compress
    delaycompress
    copytruncate
    create 0640 beszel beszel
}
EOF

touch "$LOG_FILE"
chown beszel:beszel "$LOG_FILE"
chmod 640 "$LOG_FILE"

# ========= 启动 =========
systemctl daemon-reload
systemctl enable --now "$SERVICE_NAME"

echo
echo "安装完成。"
echo "服务状态:systemctl status ${SERVICE_NAME} --no-pager"
echo "日志查看:tail -f ${LOG_FILE}"
echo "环境文件:${CONFIG_DIR}/agent.env"

CentOS系

#!/usr/bin/env bash
set -Eeuo pipefail

# ========= 用户可修改变量 =========
HUB_URL="${HUB_URL:-}"
TOKEN="${TOKEN:-}"
KEY="${KEY:-}"
SYSTEM_NAME="${SYSTEM_NAME:-}"
BESZEL_VERSION="${BESZEL_VERSION:-latest}"
INSTALL_DIR="${INSTALL_DIR:-/opt/beszel-agent}"
DATA_DIR="${DATA_DIR:-/var/lib/beszel-agent}"
CONFIG_DIR="${CONFIG_DIR:-/etc/beszel-agent}"
LOG_DIR="${LOG_DIR:-/var/log/beszel-agent}"
LOG_FILE="${LOG_FILE:-$LOG_DIR/agent.log}"
SERVICE_NAME="${SERVICE_NAME:-beszel-agent}"

# ========= 固定默认策略 =========
DISABLE_SSH="true"
LOG_LEVEL="warn"
DOCKER_HOST=""

# ========= 基础检查 =========
if [[ $EUID -ne 0 ]]; then
  echo "请用 root 运行。"
  exit 1
fi

if [[ -z "$HUB_URL" || -z "$TOKEN" || -z "$KEY" ]]; then
  echo "缺少必要变量。请至少传入:HUB_URL TOKEN KEY"
  echo '示例:'
  echo 'HUB_URL="https://hub.example.com" TOKEN="xxxxx" KEY="ssh-ed25519 AAAA..." bash install-beszel-agent-rhel.sh'
  exit 1
fi

if command -v dnf >/dev/null 2>&1; then
  PKG="dnf"
else
  PKG="yum"
fi

$PKG install -y curl ca-certificates tar logrotate

# ========= 架构识别 =========
ARCH_RAW="$(uname -m)"
case "$ARCH_RAW" in
  x86_64|amd64) ARCH="amd64" ;;
  aarch64|arm64) ARCH="arm64" ;;
  *)
    echo "不支持的架构: $ARCH_RAW"
    exit 1
    ;;
esac

# ========= 创建用户和目录 =========
if ! id beszel >/dev/null 2>&1; then
  useradd --system --home-dir "$DATA_DIR" --shell /sbin/nologin --create-home beszel
fi

mkdir -p "$INSTALL_DIR" "$DATA_DIR" "$CONFIG_DIR" "$LOG_DIR"
chown -R beszel:beszel "$DATA_DIR" "$LOG_DIR"
chmod 750 "$DATA_DIR" "$LOG_DIR"

# ========= 下载 agent =========
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT

if [[ "$BESZEL_VERSION" == "latest" ]]; then
  DOWNLOAD_URL="https://github.com/henrygd/beszel/releases/latest/download/beszel-agent_linux_${ARCH}.tar.gz"
else
  DOWNLOAD_URL="https://github.com/henrygd/beszel/releases/download/${BESZEL_VERSION}/beszel-agent_linux_${ARCH}.tar.gz"
fi

echo "下载: $DOWNLOAD_URL"
curl -fL "$DOWNLOAD_URL" -o "$TMP_DIR/beszel-agent.tar.gz"
tar -xzf "$TMP_DIR/beszel-agent.tar.gz" -C "$TMP_DIR"

install -m 0755 "$TMP_DIR/beszel-agent" "$INSTALL_DIR/beszel-agent"
chown root:root "$INSTALL_DIR/beszel-agent"

# ========= 写环境文件 =========
cat > "$CONFIG_DIR/agent.env" <<EOF
HUB_URL=${HUB_URL}
TOKEN=${TOKEN}
KEY=${KEY}
DISABLE_SSH=${DISABLE_SSH}
LOG_LEVEL=${LOG_LEVEL}
DOCKER_HOST=${DOCKER_HOST}
DATA_DIR=${DATA_DIR}
EOF

if [[ -n "$SYSTEM_NAME" ]]; then
  echo "SYSTEM_NAME=${SYSTEM_NAME}" >> "$CONFIG_DIR/agent.env"
fi

chmod 600 "$CONFIG_DIR/agent.env"
chown root:root "$CONFIG_DIR/agent.env"

# ========= SELinux 上下文(尽量处理) =========
if command -v getenforce >/dev/null 2>&1 && [[ "$(getenforce)" != "Disabled" ]]; then
  chcon -t var_log_t "$LOG_DIR" || true
  chcon -t var_log_t "$LOG_FILE" 2>/dev/null || true
fi

# ========= systemd service =========
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=Beszel Agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=beszel
Group=beszel
EnvironmentFile=${CONFIG_DIR}/agent.env
ExecStart=${INSTALL_DIR}/beszel-agent
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=${DATA_DIR} ${LOG_DIR}
StandardOutput=append:${LOG_FILE}
StandardError=append:${LOG_FILE}

[Install]
WantedBy=multi-user.target
EOF

# ========= logrotate =========
cat > "/etc/logrotate.d/${SERVICE_NAME}" <<EOF
${LOG_FILE} {
    daily
    rotate 7
    size 10M
    missingok
    notifempty
    compress
    delaycompress
    copytruncate
    create 0640 beszel beszel
}
EOF

touch "$LOG_FILE"
chown beszel:beszel "$LOG_FILE"
chmod 640 "$LOG_FILE"

# ========= 启动 =========
systemctl daemon-reload
systemctl enable --now "$SERVICE_NAME"

echo
echo "安装完成。"
echo "服务状态:systemctl status ${SERVICE_NAME} --no-pager"
echo "日志查看:tail -f ${LOG_FILE}"
echo "环境文件:${CONFIG_DIR}/agent.env"
最后修改日期: 2026年4月4日

作者