使用Docker Compose在VPS上自建Uptime Kuma和Beszel
在单台VPS上部署Uptime Kuma进行外部可用性监控,部署Beszel进行服务器指标监控。Docker Compose配置包含通知、告警、状态页面和安全加固。
你的应用已经部署了。用户正在注册。但你完全不知道它是否会在凌晨3点宕机,或者磁盘是否会在你睡觉时被填满。
本指南使用Docker Compose在单台VPS上部署两个监控工具:
- Uptime Kuma(84k+ GitHub星标)监控外部可用性:HTTP端点、TCP端口、DNS记录、SSL证书。
- Beszel(约20k星标)监控服务器内部健康状态:CPU、RAM、磁盘、网络以及每个容器的Docker统计数据。
两者加起来大约消耗150-180 MB RAM。Prometheus + Grafana + node_exporter做同样的事需要800+ MB。
前提条件: 一台运行Debian 12或Ubuntu 24.04的VPS,已安装Docker和Docker Compose。一个处理TLS的反向代理(Caddy或Nginx)。一个DNS已指向你服务器的域名。本指南使用Caddy作为反向代理示例。VPS上的Docker生产环境:会出什么问题以及如何解决
Uptime Kuma和Beszel有什么区别?
Uptime Kuma监控外部可用性。它告诉你网站、API和服务是否可以从服务器外部访问。Beszel监控服务器内部健康状态:CPU使用率、RAM、磁盘空间、网络带宽以及每个容器的Docker统计数据。一个Web服务器可能显示CPU低、内存充足,但由于防火墙配置错误或TLS证书过期而完全无法访问。你需要这两个工具。
| 功能 | Uptime Kuma | Beszel |
|---|---|---|
| 监控内容 | HTTP、TCP、DNS、ping、SSL到期、push/heartbeat | CPU、RAM、磁盘、网络、温度、Docker容器 |
| 架构 | 单容器,Web界面 | Hub + Agent(每个被监控服务器一个Agent) |
| 数据库 | SQLite(默认)或MariaDB | PocketBase(内嵌SQLite) |
| 通知渠道 | 90+(邮件、Telegram、Discord、Slack、webhooks等) | 邮件(通过PocketBase的SMTP) |
| 状态页面 | 支持,可用自定义域名的公开页面 | 不支持 |
| RAM使用 | 约80-120 MB | Hub:约10-50 MB,Agent:约25 MB |
| GitHub星标 | 84k+ | 约20k |
两个工具都不能替代对方。Uptime Kuma捕获外部故障。Beszel在资源耗尽导致外部故障之前捕获它。
监控方案占用多少RAM?
Uptime Kuma v2.x根据监控数量大约使用80-120 MB RAM。Beszel Hub增加10-50 MB,每个Agent约使用25 MB。组合方案在1 GB的VPS上可以轻松运行,总共约使用150-180 MB。作为对比,Prometheus + Grafana + node_exporter加在一起仅空闲状态就需要800+ MB。
| 方案 | 空闲RAM | 配置时间 | 适用场景 |
|---|---|---|---|
| Uptime Kuma + Beszel | 约150-180 MB | 30分钟 | 中小型自建环境 |
| Prometheus + Grafana + node_exporter | 约800 MB+ | 2-4小时 | 大规模基础设施,需自定义查询 |
| Netdata | 约300-400 MB | 15分钟 | 实时指标,单服务器 |
如何使用Docker Compose安装Uptime Kuma?
Uptime Kuma作为单个容器运行,在端口3001上提供Web界面。下面的Compose文件固定到主版本标签2,仅绑定到localhost,设置资源限制并添加健康检查。
创建项目目录:
mkdir -p /opt/uptime-kuma && cd /opt/uptime-kuma
创建Compose文件:
# /opt/uptime-kuma/compose.yaml
services:
uptime-kuma:
image: louislam/uptime-kuma:2
container_name: uptime-kuma
restart: unless-stopped
ports:
- "127.0.0.1:3001:3001"
volumes:
- ./data:/app/data
environment:
- TZ=Europe/Berlin
deploy:
resources:
limits:
memory: 256m
cpus: "0.5"
healthcheck:
test: ["CMD", "node", "/app/extra/healthcheck.js"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
127.0.0.1:3001:3001绑定确保容器只在localhost上监听。没有127.0.0.1前缀时,Docker会在所有接口上发布端口,绕过你的防火墙。修复Docker绕过UFW防火墙:4种经过测试的VPS解决方案
启动容器:
docker compose up -d
[+] Running 1/1
✔ Container uptime-kuma Started
docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
uptime-kuma louislam/uptime-kuma:2 "/usr/bin/dumb-init …" uptime-kuma 10 seconds ago Up 9 seconds (healthy) 127.0.0.1:3001->3001/tcp
(healthy)状态表示内置健康检查已通过。如果看到(starting),等待15秒让start_period完成。
使用Caddy配置反向代理
在你的Caddyfile中添加一个条目:
# /etc/caddy/Caddyfile (append)
status.example.com {
reverse_proxy localhost:3001
}
重新加载Caddy:
systemctl reload caddy
Caddy会自动从Let's Encrypt获取TLS证书。在浏览器中打开https://status.example.com。Uptime Kuma会在首次访问时提示你创建管理员账户。
立即启用2FA
创建管理员账户后,进入Settings > Security > Two-Factor Authentication并启用。Uptime Kuma的仪表板可以查看你整个基础设施的拓扑结构。任何攻破登录的人都能看到每个被监控的端点。在添加任何监控之前先设置2FA。
如何设置Beszel来监控我的VPS?
Beszel使用Hub-Agent架构。Hub是存储数据和显示指标的Web仪表板。Agent运行在每台你想监控的服务器上,将指标传回Hub。当两者运行在同一台VPS上时,它们通过Unix套接字而非网络进行通信。
创建项目目录:
mkdir -p /opt/beszel && cd /opt/beszel
创建Compose文件:
# /opt/beszel/compose.yaml
services:
beszel-hub:
image: henrygd/beszel:0.18
container_name: beszel-hub
restart: unless-stopped
ports:
- "127.0.0.1:8090:8090"
environment:
- APP_URL=https://beszel.example.com
volumes:
- ./beszel_data:/beszel_data
- ./beszel_socket:/beszel_socket
deploy:
resources:
limits:
memory: 128m
cpus: "0.25"
beszel-agent:
image: henrygd/beszel-agent:0.18
container_name: beszel-agent
restart: unless-stopped
network_mode: host
volumes:
- ./beszel_agent_data:/var/lib/beszel-agent
- ./beszel_socket:/beszel_socket
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- LISTEN=/beszel_socket/beszel.sock
- KEY=${BESZEL_KEY}
deploy:
resources:
limits:
memory: 64m
cpus: "0.15"
关于这个配置:
- Hub镜像固定到
0.18,包含CVE-2026-27734修复(v0.18.4)。固定到次版本可以防止意外的破坏性变更,同时仍能接收补丁更新。 - Agent使用
network_mode: host以读取宿主机的网络接口统计数据。这是精确带宽监控所必需的。 - Agent和Hub共享一个
beszel_socket卷用于Unix套接字通信。这避免了当两者在同一服务器上运行时暴露端口45876到网络。 - Docker套接字以只读模式(
:ro)挂载。更多信息见下面的安全部分。
Docker套接字安全
挂载/var/run/docker.sock给予Agent对Docker API的访问权限。即使使用:ro,这实际上等同于root权限,因为Docker API可以创建特权容器、读取任何容器的环境变量以及访问卷。Docker安全加固:VPS上的Rootless模式、Seccomp和AppArmor
Beszel需要套接字来收集每个容器的CPU、内存和网络统计数据。如果你不需要容器监控,完全移除Docker套接字挂载。
CVE-2026-27734(在v0.18.4中修复)展示了这个风险:经过认证的Beszel用户可以通过未净化的容器ID遍历Docker API,访问/version或/containers/json等任意端点。修复方案在构建Docker API URL之前净化所有用户输入。确保你运行的是v0.18.4或更新版本。上面Compose文件中的0.18标签解析为v0.18.4(截至2026年3月的最新补丁)。
生成Agent密钥
首先启动Hub:
cd /opt/beszel
docker compose up -d beszel-hub
在https://beszel.example.com打开Hub(配置好反向代理后,见下一部分)。创建管理员账户。在仪表板中进入Add System。Hub会显示一个公钥。复制它。
为Agent创建.env文件:
# /opt/beszel/.env
BESZEL_KEY="ssh-ed25519 AAAA... (paste the key from the hub UI)"
chmod 600 /opt/beszel/.env
现在启动Agent:
docker compose up -d beszel-agent
回到Hub界面,使用套接字路径/beszel_socket/beszel.sock添加系统。几秒钟内,CPU、RAM、磁盘和Docker容器指标就会出现。
Beszel反向代理
添加到你的Caddyfile:
# /etc/caddy/Caddyfile (append)
beszel.example.com {
reverse_proxy localhost:8090
}
systemctl reload caddy
如何在Uptime Kuma中配置通知?
Uptime Kuma支持90多种通知渠道。自建环境最常用的三种是邮件(SMTP)、Telegram和Discord。
SMTP邮件通知
进入Settings > Notifications > Setup Notification。选择**Email (SMTP)**作为类型。
| 字段 | 值 |
|---|---|
| Hostname | 你的SMTP服务器(例如smtp.example.com) |
| Port | 587(STARTTLS)或465(隐式TLS) |
| Security | STARTTLS或TLS |
| Username | 你的SMTP用户名 |
| Password | 你的SMTP密码 |
| From Email | monitoring@example.com |
| To Email | you@example.com |
保存前点击Test发送测试通知。Uptime Kuma会立即发送测试。如果失败,检查你的SMTP凭据和防火墙规则(出站端口587必须开放)。
Telegram机器人通知
- 在Telegram上给@BotFather发消息,用
/newbot创建新机器人。 - 复制机器人令牌(格式:
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11)。 - 与你的机器人开始对话并发送任意消息。
- 获取你的chat ID:在浏览器中打开
https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates。响应中的chat.id字段就是你的chat ID。 - 在Uptime Kuma中添加Telegram通知。粘贴机器人令牌和chat ID。点击Test。
Discord webhook通知
- 在你的Discord服务器中,进入Server Settings > Integrations > Webhooks > New Webhook。
- 命名(例如"Uptime Kuma"),选择频道,复制webhook URL。
- 在Uptime Kuma中添加Discord通知并粘贴webhook URL。点击Test。
勾选Default Enabled将默认通知应用于所有新监控。这样你创建的每个监控都会自动继承通知渠道,无需手动配置。
应该设置哪些Uptime Kuma监控类型?
Uptime Kuma支持多种监控类型。以下是自建环境中最有用的四种:
- HTTP(s) - 检查URL,可选匹配响应正文中的关键词。用于Web应用、API和仪表板。将关键词设为仅在应用正常时出现的字符串(例如HTML标题中的应用名称)。
- TCP - 连接到主机:端口。用于数据库(PostgreSQL的5432端口)、邮件服务器(SMTP的587端口)或任何没有HTTP端点的服务。
- DNS - 解析主机名并检查结果是否匹配预期IP。检测DNS劫持或配置错误的记录。
- Push / Heartbeat - Uptime Kuma生成一个URL。你的cron任务或备份脚本在成功时调用它。如果URL在间隔内未被调用,Uptime Kuma触发告警。这是监控没有监听端口的脚本的唯一方法。
cron任务的Push监控示例
在Uptime Kuma中创建Push监控。将heartbeat间隔设为你的cron频率加上宽限期。复制push URL。
在备份脚本末尾添加curl调用:
#!/bin/bash
# /opt/scripts/backup.sh
pg_dump mydb | gzip > /backups/mydb-$(date +%F).sql.gz
# Signal success to Uptime Kuma
curl -fsS -o /dev/null "https://status.example.com/api/push/abc123?status=up&msg=backup-ok"
如果脚本在curl行之前失败,或cron未执行,Uptime Kuma会在间隔到期后将监控标记为down。
如何在Beszel中设置CPU和磁盘告警?
Beszel告警在服务器指标超过阈值时通知你。点击仪表板中任何系统旁的铃铛图标来配置告警。
小型VPS(2-4 vCPU,4-8 GB RAM)的推荐阈值:
| 指标 | 警告 | 严重 | 原因 |
|---|---|---|---|
| CPU | 持续5分钟>70% | 持续2分钟>90% | 持续高CPU意味着失控进程或实例规格不足 |
| RAM | 持续5分钟>80% | 持续2分钟>90% | Linux在85%以上开始大量交换,性能急剧下降 |
| 磁盘 | >80% | >90% | Docker镜像、日志和数据库会悄悄增长。100%时服务崩溃 |
| 带宽 | >套餐限制的80% | >95% | 防止超额收费或限速 |
这些阈值故意低于企业默认值。小型VPS的余量更小。CPU从70%飙升到100%只需几秒,而不是几分钟。
为Beszel告警配置SMTP
Beszel使用PocketBase作为后端。SMTP通过PocketBase管理面板配置:
- 访问
https://beszel.example.com/_/(PocketBase管理URL,注意下划线)。 - 使用安装时创建的管理员凭据登录。
- 进入Settings > Mail settings。
- 启用Use SMTP mail server。
- 输入你的SMTP主机、端口、用户名和密码。
- 设置发件人地址。
- 点击Save和Send test email。
如何使用Uptime Kuma创建公开状态页面?
Uptime Kuma可以提供展示服务可用性的公开状态页面。这些页面可以在不暴露监控仪表板的情况下向用户展示运行状态。
- 进入左侧栏的Status Pages。
- 点击New Status Page。选择名称和slug(例如
status)。 - 添加分组(例如"Web服务"、"API"、"基础设施")。
- 将监控拖入各分组。
- 发布页面。可通过
https://status.example.com/status/<slug>访问。
状态页面的自定义域名
如果你想让https://status.example.com直接展示状态页面,在Uptime Kuma设置中将状态页面设为默认。根路径将显示公开页面,仪表板保留在/dashboard。
状态页面不需要认证。如果暴露端点的存在是安全隐患,不要将监控放入状态页面分组。
事件管理
当服务宕机时,Uptime Kuma自动在状态页面上显示为降级。你也可以创建手动事件:
- 进入Status Pages,选择你的页面,点击Create Incident。
- 写标题和描述(例如"数据库维护,预计15分钟")。
- 将样式设为info、warning、danger或primary。
- 发布。事件横幅出现在公开状态页面顶部。
处理完成后解决事件。Uptime Kuma保留过往事件的历史记录,让你的用户可以查看你的运维记录。
如何从外部监控我的监控方案?
如果你的VPS宕机了,Uptime Kuma和Beszel也会一起宕机。你和用户同时得知故障。解决方案:一个从不同位置监控你的Uptime Kuma实例的外部看门狗。
方案1:UptimeRobot(免费层)
- 在UptimeRobot创建免费账户。
- 添加新监控:类型HTTP(s),URL
https://status.example.com/api/status-page/heartbeat/<slug>。 - 设置检查间隔为5分钟。
- 配置邮件或Telegram通知。
/api/status-page/heartbeat/<slug>端点返回包含状态的JSON。UptimeRobot检查它,如果你的Uptime Kuma实例不可达就发出告警。
方案2:Healthchecks.io(免费层)
Healthchecks.io使用push模型。创建一个check,复制ping URL,在你的VPS上添加cron任务:
# /etc/cron.d/monitoring-heartbeat
*/5 * * * * root curl -fsS --retry 3 -o /dev/null https://hc-ping.com/your-uuid-here
如果cron ping停止到达(因为服务器宕机了),Healthchecks.io会发送告警。这覆盖了整个VPS不可达的场景。
方案3:从第二台VPS监控
如果你运行多台服务器,在另一台VPS上安装Uptime Kuma,让每个实例互相监控。这是最可靠的方案,因为你控制两个端点,不依赖第三方免费服务。
安全加固
防火墙规则
如果你在远程服务器上以独立模式运行Beszel Agent(不使用Unix套接字方式),它在端口45876上监听。只有Hub需要访问这个端口:
ufw allow from <hub-ip-address> to any port 45876 proto tcp comment "Beszel agent"
ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 80/tcp ALLOW IN Anywhere
[ 3] 443/tcp ALLOW IN Anywhere
[ 4] 45876/tcp ALLOW IN <hub-ip-address>
不要向全网开放端口45876。Agent在该端口上无需认证就暴露系统指标。它依赖Hub的SSH密钥进行验证,但网络层限制增加了纵深防御。
对于本指南中的单VPS配置,完全不需要端口45876,因为Hub和Agent通过Unix套接字通信。
Uptime Kuma:禁用API的密码认证
如果你只通过Web界面访问Uptime Kuma,通过Settings > Security > API Key禁用API访问。暴露的端点越少,需要修补的东西越少。
隐藏版本信息
Uptime Kuma和Beszel默认都在Web界面中暴露版本信息。你的反向代理不应加重这个问题。在你的Caddyfile中,Caddy默认已经省略Server头。如果你使用Nginx:
server_tokens off;
版本泄露帮助攻击者针对已知漏洞。保持最小化。
如何备份Uptime Kuma和Beszel的数据?
两个工具都使用基于SQLite的数据库。SQLite文件在应用写入时不能安全复制。使用正确的备份方法。
Uptime Kuma备份
Uptime Kuma将所有数据存储在/app/data(在Compose文件中映射为./data)。内置备份导出JSON文件:
- 进入Settings > Backup。
- 点击Export。将JSON文件保存到服务器外。
对于自动化备份,短暂停止容器或使用SQLite的在线备份:
sqlite3 /opt/uptime-kuma/data/kuma.db ".backup '/opt/backups/kuma-$(date +%F).db'"
Beszel备份
Beszel使用PocketBase。备份数据目录:
sqlite3 /opt/beszel/beszel_data/data.db ".backup '/opt/backups/beszel-$(date +%F).db'"
将备份存储在服务器外。一个磁盘挂掉就丢失历史记录的监控方案等于什么都没监控。在VPS上备份和恢复Docker卷
如何安全更新Uptime Kuma和Beszel?
固定到次版本号,不要用latest。这可以防止破坏性变更在你不知情的情况下到来。
# Update Uptime Kuma
cd /opt/uptime-kuma
docker compose pull
docker compose up -d
[+] Pulling 1/1
✔ uptime-kuma Pulled
[+] Running 1/1
✔ Container uptime-kuma Started
docker compose ps
检查STATUS列是否显示(healthy)。如果新版本导致问题,在compose.yaml中固定到之前的版本并重新创建:
# In compose.yaml, change the image tag to the previous version:
# image: louislam/uptime-kuma:2.2.1
docker compose up -d
同样的流程适用于Beszel。更新前务必先备份。
镜像固定策略
louislam/uptime-kuma:2标签跟踪最新的2.x版本。这很方便,但意味着docker compose pull可能在没有预警的情况下从2.2.1跳到2.3.0。在生产环境中,固定到具体的次版本:
image: louislam/uptime-kuma:2.2
拉取前查看发布说明。Uptime Kuma在GitHub发布版本。Beszel同样在其发布页面上发布。
订阅两个仓库的发布通知(GitHub上的Watch > Custom > Releases),以便了解安全补丁何时发布。
故障排除
Uptime Kuma在docker compose ps中显示(unhealthy):
docker compose logs uptime-kuma --tail 50
常见原因:SQLite数据库损坏(从备份恢复)、端口冲突(3001上有其他服务)或内存不足(增加资源限制)。
Beszel Agent无法连接到Hub:
docker compose logs beszel-agent --tail 50
检查.env中的KEY是否与Hub的Add System对话框中显示的密钥匹配。如果使用Unix套接字,验证共享卷的挂载路径在两个服务上是否一致。
Beszel不显示Docker容器统计数据:
Docker套接字挂载缺失或Docker套接字路径错误。检查:
ls -la /var/run/docker.sock
srw-rw---- 1 root docker 0 Mar 20 10:00 /var/run/docker.sock
套接字必须存在且容器必须有读取权限。Compose文件中的:ro挂载处理了这一点。
通知未到达:
对于SMTP:检查出站端口587(或465)是否被你的托管提供商阻止。一些提供商默认阻止出站SMTP。测试:
nc -zv smtp.example.com 587
Connection to smtp.example.com 587 port [tcp/submission] succeeded!
对于Telegram:验证机器人令牌和chat ID。chat ID必须是数字,不是机器人用户名。
高内存使用:
监控数量很重要。Uptime Kuma v2.x空闲时大约使用100 MB。每个HTTP监控增加连接状态。如果在256 MB内存限制下超过100个监控,增加限制或分散到多个实例。
检查实际使用情况:
docker stats --no-stream uptime-kuma beszel-hub beszel-agent
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
abc123 uptime-kuma 0.15% 99MiB / 256MiB 38.67% 1.2kB / 2kB 0B / 0B 8
def456 beszel-hub 0.08% 10MiB / 128MiB 7.81% 1kB / 1.5kB 0B / 745kB 8
ghi789 beszel-agent 0.05% 22MiB / 64MiB 34.38% 0B / 0B 0B / 0B 5
准备好亲自尝试了吗?
在Virtua Cloud VPS上部署你的监控方案。 →