自建 Umami 网站统计:替代 Google Analytics,数据握在自己手里(2026)

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

上面这段是结论块。下面是架构、部署步骤、埋点方式,以及 v2 版本三个真会踩的坑。

Umami 统计的数据链路

自建 Umami 和 Google Analytics 差在哪?

结论:差在「数据归谁」和「合规成本」。 GA 免费但数据归 Google、需要弹 Cookie 同意条、被广告拦截器大量屏蔽;Umami 自建,数据归你、不需要 Cookie 同意条、拦截率低。对账如下:

维度 Google Analytics 自建 Umami
数据归属 Google 你自己
Cookie 需要同意弹窗 不用,无 Cookie
被广告拦截器屏蔽 严重(常见 20%+ 漏计) 极少
数据保留 有上限、会聚合丢失细节 你自己的库,想留多久留多久
成本 免费 一台 VPS(或复用现有)
上手成本 低 首次部署约 15 分钟

什么时候不值得自建:你需要广告投放归因、需要 GA 与 Google Ads 打通、或者团队已经在用 GA 生态时,自建会增加割裂。纯粹的「看流量趋势」需求,Umami 足够且更省心。

自建 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 多服务编排 里总结的模式完全一致。

步骤 1:写 compose.yml

第二步:启动并立刻改默认密码

$ docker compose up -d
$ docker compose logs -f umami     # 等它把数据库迁移跑完

迁移完成后浏览器访问 http://你的IP:3000。v2 的默认账号是 admin,密码是 umami——登录后第一件事就是改掉它。

步骤 2:启动并改默认密码

第三步:反代 + 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。两个名字都留着的镜像版本也有,但别依赖它。

步骤 4:看数据并导出

第五步:多站点与共享链接

  • 多站点:一个 Umami 实例可以管多个网站,每个站点一个 website-id,同一个账号下切换查看。
  • 共享链接:Share 里生成一条公开只读链接,发给同事看数据时不用给对方开账号。
  • 导出:Reports 支持导出 CSV,周复盘时直接在表里算留存和趋势。

v2 版本三个真会踩的坑

  1. 默认密码 admin/umami 忘了改。 面板一暴露到公网,扫描器几分钟内就会用这对默认值登进来。既然要反代,就把反代和改密码一起做掉,别等。

  2. 数据库迁移没跑完就以为部署成功。 Umami v2 首次启动会自动跑迁移,日志里有 migration 字样。如果你用的是老镜像或自定义镜像,可能需要手动执行迁移命令(v1 时代的 docker exec -it umami yarn migrate 或 v2 的等价命令,按你镜像版本查 README)。日志没显示迁移完成前,登录会报 500。

  3. 单页应用(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 更合适的默认选择。

相关阅读

上一篇 MySQL 备份与恢复实战:mysqldump + binlog 恢复到误删前一刻(2026)