Umami 自托管:给静态博客加访问统计的完整复盘
这个博客一直没有任何访问统计——不知道哪些文章有人读、来自哪个渠道、用什么设备看的。不是不需要,而是没有找到一个合适的方案。
Google Analytics 不在考虑范围。剩下四个选项:GoatCounter、Plausible、Umami、Matomo。我花了一晚选型,又花了一晚部署。以下是整个过程——从决策到上线。
选型:四选一
四个候选都是隐私优先、自托管、无 cookie。快速对照:
| GoatCounter | Plausible | Umami | Matomo | |
|---|---|---|---|---|
| 语言 | Go 单二进制 | Elixir | Node.js | PHP |
| 数据库 | SQLite | PG + ClickHouse | PostgreSQL | MySQL |
| 内存 | ~30 MB | >2 GB | ~300 MB | ~800 MB |
| GitHub | 5.8k ★ | 27.3k ★ | 37.3k ★ | 21.6k ★ |
| Docker | 🚫 无官方镜像 | ✅ Compose | ✅ Compose | ✅ 镜像 |
| 脚本 | 3.5 KB | <1 KB | 2 KB | 22 KB |
| 许可证 | EUPL | AGPLv3 | MIT | GPLv3 |
淘汰逻辑很直接:
- Matomo 太重,PHP + MySQL 体系,功能过剩
- Plausible CE 社区版半年一发、砍高级功能,自托管需要 ClickHouse(2GB 起步)
- GoatCounter 最轻量,但没有开箱的看板系统,API 偏弱
Umami 在三个维度上最合适:社区最大(37.3k stars)、MIT 许可证最开放、Docker Compose 一键部署,资源和功能之间平衡最好。
部署
架构
analytics.example.com (Caddy 反代) → 127.0.0.1:3007 (Umami)
→ 127.0.0.1:5432 (PostgreSQL 17)
Umami 需要 PostgreSQL 12.14+。服务器上已有一个 PG 10(给 Miniflux 用),版本不够,直接起了一个新的 PostgreSQL 17 Alpine 容器。
Docker Compose
/opt/stacks/umami/docker-compose.yml:
services:
umami:
image: docker.umami.is/umami-software/umami:postgresql-latest
ports:
- "127.0.0.1:3007:3000"
environment:
DATABASE_URL: postgresql://umami:<密码>@umami-db:5432/umami
APP_SECRET: <随机 hex 64>
CLIENT_IP_HEADER: X-Forwarded-For
TRACKER_SCRIPT_NAME: u
restart: unless-stopped
depends_on:
umami-db:
condition: service_healthy
umami-db:
image: postgres:17-alpine
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: <密码>
volumes:
- ./pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U umami"]
interval: 5s
timeout: 5s
retries: 5
三个环境变量的用意:
| 变量 | 值 | 原因 |
|---|---|---|
CLIENT_IP_HEADER | X-Forwarded-For | Caddy 反代后正确记录访客 IP,否则所有 IP 都显示 127.0.0.1 |
APP_SECRET | openssl rand -hex 32 | 每个安装需要独立的密钥,用于加密认证令牌 |
TRACKER_SCRIPT_NAME | u | 追踪脚本名从默认的 script.js 改成 u,减少被广告拦截器屏蔽的概率 |
Caddy 反代
/etc/caddy/Caddyfile 追加:
analytics.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:3007
tls lss6378@gmail.com
}
重载 Caddy 时发现一个意外:token.logfun.xyz 那段语法早就有问题——多了两个多余的 },被 caddy fmt 修掉了。之前居然能跑,可能是 Caddy 对这种情况有一定容错。
部署细节
用 docker-compose(注意这个系统是独立包不是 Docker 插件),起容器后默认登录 admin / umami,登录后第一时间改了密码。
第一次 docker pull 超慢,Docker Hub 在国内被墙。Docker 守护进程已配了 mihomo 代理(http://127.0.0.1:7890),但 docker-compose 运行时没有继承代理环境变量。加 HTTPS_PROXY 和 HTTP_PROXY 后重拉很快。
端口选了 3007——3000 是 new-api、3001 是 uptime-kuma、3002 是 headplane,得避开。
配置
创建网站和看板
Umami 支持通过 REST API 完成所有 UI 操作。创建网站:
curl -s https://analytics.example.com/api/websites \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"lls_blog","domain":"blog.logfun.xyz"}'
拿到了 websiteId。
看板(Boards)是 v3.1.0 的新功能——可以拖拽组件组成自定义仪表盘。API 创建看板时注意两点:
websiteId要嵌套在parameters对象里,不是顶层字段- 更新看板用
POST /api/boards/:id(不是 PUT 或 PATCH)
看板的组件布局同样通过 parameters.rows 字段写入。每个 row 包含 columns,每个 column 有一个 component。我建了四个看板:
| 看板 | 组件 |
|---|---|
| 概览 | website-metrics-bar(指标栏)+ website-chart(趋势图)+ metrics-table(页面浏览) |
| 热门文章 | metrics-table × 2(页面访问排行、页面标题排行) |
| 来源 | metrics-table × 2(来源域名、UTM 来源) |
| 访客 | world-map + metrics-table × 3(国家/地区、浏览器、设备) |
可用组件类型:
| 组件 | 说明 |
|---|---|
website-metrics-bar | 汇总指标卡片 |
website-chart | 时序折线图 |
metrics-table | 可配置维度表格 |
world-map | 地理分布图 |
weekly-traffic | 周流量热力图 |
events-chart | 自定义事件图 |
text-block | 自由文本 |
注入追踪脚本
Zola 的 serene 主题通过 templates/_head_extend.html 注入 JS。在文件末尾加一行:
<script defer src="https://analytics.example.com/u" data-website-id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"></script>
注意这里是 /u 不是 /u.js——TRACKER_SCRIPT_NAME: u 会让 Umami 把追踪脚本挂在 https://analytics.example.com/u,但如果请求 u.js 会 404。源码里 TRACKER_SCRIPT_NAME 的值和请求路径直接对应,不加后缀名。
defer 属性保证脚本不阻塞页面渲染。
踩过的坑
几个卡住超过五分钟的问题:
-
Docker pull 超时:
docker-compose不继承守护进程的代理环变,需要显式传HTTPS_PROXY。或者直接用docker compose插件版会好一些。 -
端口冲突:选端口前先用
ss -tlnp扫一遍,别猜。 -
API 方法搞错:看板更新是
POST不是PUT/PATCH,Next.js 路由里POSThandler 同时处理创建和更新。第一次用 PUT 试了几次才发现。 -
Prisma 报
description缺失:创建看板时description字段在 API 里是可选的,但 Prisma 校验时会报缺失。填上内容就过。 -
Caddyfile 格式问题:
caddy validate在重载前一定要跑,能发现隐藏的语法问题。
效果
部署完成后,analytics.example.com 访问看板即可查看所有统计数据。追踪脚本 2KB,defer 加载不阻塞页面,不影响 Lighthouse 评分。数据存在自己的 PG 里,不经第三方。
整个流程涉及的工具层:
选型 → Docker → Caddy → API → Zola 模板
每一步都有现成的工具和文档,唯一花时间的是弄清楚 Umami 的 API 行为和那些不起眼的参数细节。
参考