Umami 是一个开源、轻量、注重隐私的网站统计工具,用 Docker 大约 15 分钟就能自建完成:它不写 Cookie、不采集个人身份信息、页面埋点只有一个 <script> 标签,数据全部存在你自己的 PostgreSQL 里。 它能给出访客数、浏览量、来源、国家、设备、浏览器、访问时长和实时在线——正好覆盖了个人站复盘需要的那几个指标,而数据不再进 Google 的服务器。我在 2026 年 10 月把两个站从 Google Analytics 切到自建的 Umami v2,一个容器加一个数据库,内存占用长期在 200MB 以内。
上面这段是结论块。下面是架构、部署步骤、埋点方式,以及 v2 版本三个真会踩的坑。

自建 Umami 和 Google Analytics 差在哪?
结论:差在「数据归谁」和「合规成本」。 GA 免费但数据归 Google、需要弹 Cookie 同意条、被广告拦截器大量屏蔽;Umami 自建,数据归你、不需要 Cookie 同意条、拦截率低。对账如下:
| 维度 | Google Analytics | 自建 Umami |
|---|---|---|
| 数据归属 | 你自己 | |
| Cookie | 需要同意弹窗 | 不用,无 Cookie |
| 被广告拦截器屏蔽 | 严重(常见 20%+ 漏计) | 极少 |
| 数据保留 | 有上限、会聚合丢失细节 | 你自己的库,想留多久留多久 |
| 成本 | 免费 | 一台 VPS(或复用现有) |
| 上手成本 | 低 | 首次部署约 15 分钟 |
什么时候不值得自建:你需要广告投放归因、需要 GA 与 Google Ads 打通、或者团队已经在用 GA 生态时,自建会增加割裂。纯粹的「看流量趋势」需求,Umami 足够且更省心。
自建 Umami 五个步骤

第一步:写 compose.yml
Umami v2 用 PostgreSQL,两个服务一个网络:
services:
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
restart: always
ports:
- "127.0.0.1:3000:3000"
environment:
DATABASE_URL: postgresql://umami:换成强密码@db:5432/umami
APP_SECRET: 换成一段随机串
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: 换成强密码
volumes:
- umami-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U umami -d umami"]
interval: 5s
retries: 10
volumes:
umami-db:
注意 ports 写的是 127.0.0.1:3000:3000——只绑回环,等反代接 HTTPS。 直接 3000:3000 会把面板暴露到公网,等着被扫。这种「服务名即主机名、healthcheck 保证顺序」的写法,和我在 Docker Compose 多服务编排 里总结的模式完全一致。

第二步:启动并立刻改默认密码
$ docker compose up -d
$ docker compose logs -f umami # 等它把数据库迁移跑完
迁移完成后浏览器访问 http://你的IP:3000。v2 的默认账号是 admin,密码是 umami——登录后第一件事就是改掉它。

第三步:反代 + HTTPS
必须上 HTTPS,否则你会被浏览器的「不安全内容」提示恶心到,埋点脚本也可能被拦。 用一个最小 Nginx 配置:
server {
listen 443 ssl;
server_name stats.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
}
证书用 certbot --nginx -d stats.example.com 一条命令签发,续期管理和 Nginx 反代 + Let's Encrypt 那篇完全一样。
如果你的域名前面还挂了 Cloudflare,记得给这个子域加一条 Bypass Cache 规则——否则 /script.js 和上报接口 /api/send 可能被边缘缓存,统计就会失真。配置方式和 Cache Rules 实战 里「给登录态放行」的思路一致,把 stats.example.com 整体排除缓存即可。
第四步:站点埋点
在 Umami 面板 Websites → 新建站点,拿到 website-id,然后把这段放进你的站点模板(放在 </head> 前):
<script defer src="https://stats.example.com/script.js"
data-website-id="你的website-id"></script>
这里有个 v2 的改名坑:老版本追踪脚本叫 umami.js,v2 改成 script.js。如果你照着旧教程写 umami.js,会拿不到数据且控制台报 404。两个名字都留着的镜像版本也有,但别依赖它。

第五步:多站点与共享链接
- 多站点:一个 Umami 实例可以管多个网站,每个站点一个
website-id,同一个账号下切换查看。 - 共享链接:
Share里生成一条公开只读链接,发给同事看数据时不用给对方开账号。 - 导出:
Reports支持导出 CSV,周复盘时直接在表里算留存和趋势。
v2 版本三个真会踩的坑
-
默认密码
admin/umami忘了改。 面板一暴露到公网,扫描器几分钟内就会用这对默认值登进来。既然要反代,就把反代和改密码一起做掉,别等。 -
数据库迁移没跑完就以为部署成功。 Umami v2 首次启动会自动跑迁移,日志里有
migration字样。如果你用的是老镜像或自定义镜像,可能需要手动执行迁移命令(v1 时代的docker exec -it umami yarn migrate或 v2 的等价命令,按你镜像版本查 README)。日志没显示迁移完成前,登录会报 500。 -
单页应用(SPA)路由切换不计入浏览量。 Umami 默认按页面加载计数,用前端路由的 SPA 切页不会触发。需要在路由切换时手动上报:
js
window.umami?.track('pageview')
在你的路由守卫里调用一次即可。这是静态博客(比如 Cloudflare Pages 上跑的 Astro / Hugo 多页站)不常遇到,但 SPA 一定会遇到。
统计和监控要分开看
Umami 回答「谁来过、看了什么」,Uptime-Kuma 回答「服务还活着吗」——这两件事别指望一个工具。 我把两者都自建:Umami 管流量分析,Uptime-Kuma 管可用性告警。它们都跑在同一个 compose 网络里,资源占用加起来不到 400MB,一台小 VPS 完全够。
Umami 自建常见问题(2026)
Q:Umami 免费吗?
A:完全开源免费(MIT),自建没有任何功能限制。官方也提供一个付费的云托管版,但自建就是全功能。
Q:Umami 需要什么配置?
A:1 核 1G 的 VPS 足够。应用本身很轻,主要内存开销在 PostgreSQL;两个容器长期占用约 200–400MB。
Q:为什么我的数据比 Google Analytics 少/多?
A:通常 Umami 更多,因为 GA 被广告拦截器屏蔽得多。两者计数口径本来不同(会话定义、去重窗口),只要看趋势一致就行,不必强求数字相同。
Q:数据能迁移吗?
A:能。备份 PostgreSQL 数据库(pg_dump)就带走了全部数据;换服务器时把卷或 dump 迁过去即可。备份方式和 Docker Compose 里的卷备份 一样。
Q:埋点脚本加上了但面板没数据?
A:按顺序查——① 浏览器控制台有没有 404(脚本名写错);② Network 里 /api/send 是否返回 200(被反代或 Cloudflare 拦了);③ data-website-id 是否和面板里的一致。三者覆盖 95% 的"加了没数"。
一句总结
自建 Umami 的核心价值是「把网站数据的所有权拿回来」:一次 15 分钟的部署,换来无 Cookie、不采集个人信息、数据存在自己库里的统计——对个人博客和内容站,这是比 GA 更合适的默认选择。