未坐标系

Uptime Kuma 自托管监控服务

用 Uptime Kuma + kuma-mieru 搭一套轻量、好看、可长期维护的监控面板

2026/07/04
loading

用 Uptime Kuma + kuma-mieru 搭一套轻量、好看、可长期维护的监控面板

很多人第一次接触自托管监控,都会先从 Uptime Kuma 开始:安装简单、支持的监控类型多、Telegram 告警也很好接,几乎可以算是轻量监控的默认答案。

这篇文章就把一套比较稳、也比较适合长期使用的方案整理一下。


一、Uptime Kuma 和 kuma-mieru 分别是什么

Uptime Kuma 是一款开源自托管的监控工具,核心就一件事:定期去戳一下你的服务,看它活没活着,挂了就赶紧告诉你。通知方式也现成,Telegram、Email、Webhook 这些直接就能用,自己还带一个状态页。部署简单、社区大,算是轻量监控里相当稳妥的选择。

它能盯的东西很多,常见的几种探测方式都覆盖了:

Pasted image 20260704202351.png

kuma-mieru 是一款基于 Next.js 16、TypeScript 和 Recharts 构建的第三方 Uptime Kuma 监控仪表盘。解决了 Uptime Kuma 内建公开状态页面不够直观、没有延迟图表等痛点。

Pasted image 20260704215054.png

Pasted image 20260704213522.png


二、部署 Uptime Kuma

1)准备目录

mkdir -p /opt/uptime-kuma
cd /opt/uptime-kuma

2)编写 docker-compose.yml

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

3)启动服务

docker compose up -d

4)本地验活

curl -I http://127.0.0.1:3001

正常情况下会看到类似:

  • 302 Found
  • 跳转到 /dashboard

这说明 Kuma 已经起来了。


三、为 Uptime Kuma 配置 Nginx 反代

假设你的后台域名是:

  • uptime.example.com

Nginx 示例配置:

点击展开
server {
    listen 80;
    server_name uptime.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name uptime.example.com;

    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

重载 Nginx:

nginx -t && systemctl reload nginx

然后访问:

  • https://uptime.example.com

如果能正常打开 Kuma 登录页,就说明主链路已经通了。


四、初始化 Kuma 后,建议先做这几件事

1)创建管理员账号

这是首次使用的基础步骤,就不展开了。

2)打开 2FA

如果后台是公网可访问的,强烈建议开启二步验证。

3)配置 Telegram 通知

Kuma 自带 Telegram 通知已经很好用,建议:

  • 消息格式选 HTML
  • 不建议选 MarkdownV2(容易被域名、下划线、错误文本之类的内容搞翻)

Pasted image 20260704204706.png

一个比较顺眼、也比较稳的 Liquid 模板示例:

{% assign s = status | downcase %}
{% if s contains "up" %}
✅ <b>服务恢复</b>

<b>服务</b>:<code>{{ name }}</code>
<b>状态</b>:{{ status }}
<b>目标</b>:{{ hostnameOrURL }}
<b>说明</b>:{{ msg }}
{% else %}
🚨 <b>服务异常</b>

<b>服务</b>:<code>{{ name }}</code>
<b>状态</b>:{{ status }}
<b>目标</b>:{{ hostnameOrURL }}
<b>原因</b>:{{ msg }}
{% endif %}

五、添加监控项

1)监控网站和 API 的 /healthz、某个公开端点

Pasted image 20260704205531.png
监控类型:HTTP(s)
显示名称:会显示在公开显示面板上,如果不想暴露网址就自定义一个名称
其余设置选项推荐默认,也可按需选择

2)监控定时任务、远端脚本

Pasted image 20260704211310.png

创建后会得到一条 Push URL,类似:

https://uptime.example.com/api/push/<token>

在远端机器主动上报

最简单的写法:

curl -fsS "https://uptime.example.com/api/push/<token>?status=up&msg=OK"

如果你想根据服务状态决定上报内容,可以写成:

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

PUSH_BASE="https://uptime.example.com/api/push/<token>"
SERVICE_NAME="my_service.service"

if systemctl is-active --quiet "$SERVICE_NAME"; then
  status="up"
  msg="service_active"
else
  status="down"
  msg="service_inactive"
fi

curl -fsS \
  --get \
  --data-urlencode "status=${status}" \
  --data-urlencode "msg=${msg}" \
  "$PUSH_BASE" >/dev/null

相比 cron,我更推荐 systemd timer,因为:

  • 状态更好查
  • 更像正式服务
  • 排障更顺

示例:

service

[Unit]
Description=Push service health to Uptime Kuma
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/service-kuma-heartbeat.sh

timer

[Unit]
Description=Run service Kuma heartbeat every 300 seconds

[Timer]
OnBootSec=60
OnUnitActiveSec=300
AccuracySec=15
Unit=service-kuma-heartbeat.service

[Install]
WantedBy=timers.target

启动命令:

systemctl daemon-reload
systemctl enable --now service-kuma-heartbeat.timer

Push Monitor 提醒:超时窗口别和上报频率设得太接近。
经验法则是 超时窗口至少设成上报频率的 2 倍,稳一点就设 3 倍——比如 300 / 900
为什么这么设、踩线会发生什么,详见文末附录一。


六、配置 Uptime Kuma 的公开状态页

1)新建一个 Status Page

例如:

  • Title:My Status
  • Slug:my-status

生成后地址通常类似:

  • https://uptime.example.com/status/my-status

对应的 API 地址一般类似:

  • https://uptime.example.com/api/status-page/my-status

2)记住这个重点

Monitor 不会自动出现在状态页里。

创建了监控项,还得手动把它加入某个 status page group,它才会出现在公开状态页和 kuma-mieru 里。这个坑很反直觉,详细见文末附录二。

3)分组建议

不要把所有服务都堆在一个组里。推荐按用途分:

  • Public Sites
  • Core Infra
  • Bots
  • External

可以拖动调整分组和顺序,保存后与面板显示页同步

Pasted image 20260704212628.png


七、部署 kuma-mieru

推荐使用 vercel 或者 Cloudflare Workers 部署(参考Alice39s/kuma-mieru

以下是本地部署方案:

1)准备目录

mkdir -p /opt/kuma-mieru
cd /opt/kuma-mieru

2)编写 .env

UPTIME_KUMA_URLS=https://uptime.example.com/status/my-status
KUMA_MIERU_TITLE=My Status
KUMA_MIERU_DESCRIPTION=My uptime dashboard
KUMA_MIERU_ICON=/icon.svg
KUMA_MIERU_EDIT_THIS_PAGE=false
KUMA_MIERU_SHOW_STAR_BUTTON=false
ALLOW_INSECURE_TLS=false
ALLOW_EMBEDDING=false

3)编写 docker-compose.yml

services:
  kuma-mieru:
    image: ghcr.io/alice39s/kuma-mieru:1
    container_name: kuma-mieru
    restart: unless-stopped
    env_file:
      - .env
    ports:
      - "127.0.0.1:3883:3000"

注意:不同镜像版本内部端口可能存在差异,实际部署时以镜像说明为准。

4)启动

docker compose up -d

5)本地验活

curl -I http://127.0.0.1:3883
curl http://127.0.0.1:3883/api/health

如果本地能正常返回,说明展示层已经起来了。


八、给 kuma-mieru 做 Nginx 反代

假设状态页域名是:

  • status.example.com

Nginx 示例配置:

点击展开
server {
    listen 80;
    server_name status.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name status.example.com;

    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://127.0.0.1:3883;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

重载:

nginx -t && systemctl reload nginx

然后访问:

  • https://status.example.com

附录:实战时踩过的几个坑

下面这几个坑,都是这套方案里很值得提前知道的。

附录一、Push Monitor 不要把“上报频率”和“超时窗口”设成一样

这是最典型、也最容易让人误判的一类问题。

现场现象

服务明明没挂,但监控里会出现这种节奏:

  • Down
  • 紧接着又 Up
  • 过几分钟再 Down
  • 然后又 Up

如果刚好是每隔 5 分钟抖一次,十有八九就是时间窗口踩线了。

根因

比如:

  • 远端脚本每 300 秒 push 一次
  • Kuma 也按 300 秒 判 heartbeat 超时

那只要有一点点误差:

  • systemd timer 晚几秒
  • 网络慢几秒
  • Kuma 自己检查点早几秒或晚几秒

它就会在下一次 heartbeat 抵达前,先一步把服务判成:

  • No heartbeat in the time window

然后新 heartbeat 一到,又立刻恢复成 Up

正确做法

让两边明显错开。

例如:

  • Push 频率:300 秒
  • 超时窗口:900 秒

经验法则可以直接记成一句:

Push Monitor 的超时窗口,至少设成上报频率的 2 倍;想更稳,就设 3 倍。

也就是:

  • 300 / 600:能用
  • 300 / 900:更稳

如果只是拿来监控普通服务是否存活,我会更推荐直接上 300 / 900


附录二、Monitor 建好了,不代表它会自动出现在状态页里

这个坑特别容易踩,因为它很反直觉。

很多人会默认以为

只要在 Uptime Kuma 后台里新建了一个 monitor,它就应该:

  • 后台能看到
  • 状态页也能看到
  • kuma-mieru 当然也该能看到

但实际上不是。

真正的逻辑是

  • Monitor 列表 是后台的监控项清单
  • Status Page 是你手动挑出来要公开展示的那部分 monitor
  • kuma-mieru 读取的是 Kuma 的公开状态页,不是后台 monitor 总表

所以只要少了“加入状态页分组”这一步,就会出现:

  • 后台有 monitor
  • 状态页没有它
  • kuma-mieru 也没有它

正确理解

创建 monitor 和把 monitor 加入 status page,是两步,不是一步。

如果你发现某个服务明明已经在 Kuma 后台里了,但前台展示死活不出来,先别怀疑 kuma-mieru,先检查它有没有被加入对应的 status page group。


附录三、kuma-mieru 读取的是公开状态页,不是 Kuma 私有后台

这一点值得单独拎出来强调,因为很多后续误会都从这里来。

  • kuma-mieru 读取的是类似:
    • /status/<slug>
    • /api/status-page/<slug>

也就是说,它依赖的是:

  1. 你先在 Kuma 里创建了公开状态页
  2. 这个状态页已经发布
  3. monitor 已经被加入这个状态页
  4. kuma-mieru 配置里填的是正确的状态页 URL

这意味着什么

它是个很清晰的“展示层”思路:

  • Kuma 后台负责监控与管理
  • 状态页负责挑选公开内容
  • kuma-mieru 负责把公开内容展示得更好看

也正因为如此,kuma-mieru 这层本身并不会替你决定“哪些 monitor 要公开”。这个决定仍然在 Kuma 的 status page 那一层完成。


附录四、Telegram 通知模板想顺眼,重点不只是模板本身

很多人第一次玩 Kuma 自定义通知,都会先去折腾模板语法。但真要让告警看起来舒服,通常影响最大的其实是两件事:

  1. 消息格式选 HTML
  2. monitor 名称本身别太难看

为什么推荐 HTML

因为监控消息里经常会带这些东西:

  • 域名
  • URL
  • 错误信息
  • 下划线
  • 括号
  • 连字符

如果用 MarkdownV2,很容易在转义规则出问题。

为什么命名也很重要

模板再顺,如果 monitor 名称本身还是:

  • rss_bot
  • cli.example.com
  • service_check_01

观感也会被拖下去。

更适合展示和告警的命名会像:

  • 📰 Nodeseek RSS
  • 🤖 CPA API
  • 🌐 Blog
  • 🧭 OneNav
  • 📈 Umami

所以别把“模板优化”理解成只改模板字符串,它其实是:

模板 + 命名 + 分组,一起决定最终观感。


附录五、不要把“端口监听正常”误当成“整条链路没问题”

这类问题也很常见。

比如你本地去 curl 容器端口,发现:

  • 127.0.0.1:3001 正常
  • 127.0.0.1:3883 正常

这只能说明:

  • 容器本身活着
  • 本机监听没问题

但对外访问是否正常,还取决于:

  • Nginx 是否命中正确的 server_name
  • TLS 证书是否正常
  • 反代配置是否写对
  • 域名是否真正走到了预期站点

尤其是在一台宿主机上挂了多个站点、默认站点还在工作的情况下,“访问能打开页面”不一定等于“命中了你以为的那个 vhost”

所以遇到展示不对、域名结果怪怪的场景,排查顺序最好是:

  1. 先看本地端口
  2. 再看 Nginx 配置
  3. 再用 Host / SNI 方式确认实际命中链路

参考链接

CATALOG