【实用的开源工具】Umami:用 Docker 和 PostgreSQL 自托管的网站统计

阅读约 13 分钟
【实用的开源工具】Umami:用 Docker 和 PostgreSQL 自托管的网站统计

「实用的开源工具」系列:我只收集值得用的开源项目,写清楚它能做什么、怎么部署、有哪些坑,并在文末标明出处。内容整理自官方文档,核对日期 2026-10-31,具体版本以官方为准。文中的命令来自官方 README、文档站和仓库里的 Compose 文件,我没有在本文里逐条实际运行过,上线前请先在测试环境走一遍。

项目信息卡

项目说明
名称Umami
做什么开源的网站与产品分析平台,官方 README 称其“隐私优先”,不使用 cookie,可自托管也可用官方云服务
许可证MIT
仓库github.com/umami-software/umami
文档docs.umami.is(umami.is/docs 会跳转到这里)
最近版本v3.4.1(2026-10-23 发布,查询于 2026-10-31)。官方发布说明称这是一个以安全修复为主的版本,建议所有自托管用户升级
状态仓库未归档,最近一次提交在 2026-10-31
数据库只支持 PostgreSQL,官方 FAQ 写的最低版本是 v12.14
运行方式官方 docker compose(应用加 PostgreSQL 两个容器),也可以从源码构建(需要 Node.js 和 pnpm)
默认账号首次建库会创建用户名 admin、密码 umami 的管理员账号,官方要求第一次登录后立刻改密码
可选组件Redis(REDIS_URL,不设就不启用相关功能)
云版差异官方文档表格里写明:邮件报告只在云版提供,其他列出的分析功能自托管版也有

官方说明里值得注意的几点

  • 官方 FAQ:Umami 的网页跟踪脚本不使用 cookie;是否需要弹 cookie 提示取决于你用的其他服务和适用的法规。
  • 官方 FAQ:自托管数据默认永久保留,除非你手动删除。
  • 官方环境变量文档:Umami 会收集“完全匿名的遥测数据”用于改进应用,可以用 DISABLE_TELEMETRY 关闭;也有 DISABLE_UPDATES(关闭新版本检查)和 PRIVATE_MODE(关闭所有外部网络请求,网站图标也会因此不显示)。

它能做什么

  • 基础统计:页面浏览量、访客、来源、地区、设备、实时活动。
  • 营销与转化:UTM 活动、链接、像素、目标、漏斗、归因、留存、收入报告。
  • 行为分析:自定义事件、用户旅程、会话回放、热力图(版本要求见各功能页的标注)。
  • 团队与共享:看板(boards)、团队、共享链接、API,以及一个默认关闭的官方只读 MCP 服务(需要 MCP_ENABLED=1)。
  • 两步验证:v3.3.0 起支持 TOTP 双因素认证,需要先配置密钥(见下文)。

不太适合的场景:你的服务器上只有 MySQL,不想再多装一个 PostgreSQL;或者你需要邮件报告这类官方只在云版提供的功能。

和同类项目怎么选

下面只列我在各项目官方文档里核对过的事实,不涉及性能和“谁更好”的比较:

UmamiPlausible CEMatomo
许可证MITAGPL-3.0GPL-3.0(或更高)
数据库PostgreSQLPostgreSQL + ClickHouseMySQL 或 MariaDB
cookieFAQ 称网页跟踪脚本不使用 cookieREADME 称不使用 cookie默认跟踪代码会设置第一方 cookie,可在后台强制无 cookie
运行环境Docker 或 Node.jsDocker(官方流程)PHP + Web 服务器,官方也有 Docker 镜像

怎么选:手上只想多装“一个应用加一个 PostgreSQL”,Umami 的部署最直接;需要 ClickHouse 的统计引擎可以看 Plausible;习惯 PHP 加 MySQL 的传统环境可以看 Matomo。许可证上,MIT 对二次开发和商用的限制最少,AGPL 和 GPL 有各自的开源义务,用于商业产品前请自己读条款。至于是否需要弹同意提示,请按你所在地区的法规自己判断,官方说“无 cookie”不等于不用考虑合规。

部署

官方安装页面给了几种方式:源码构建、Docker Compose、预构建 Docker 镜像,以及第三方维护的 Kubernetes Helm chart。这里只讲最常用的 Docker Compose。

准备

  • 一台装好 Docker 和 Docker Compose 的服务器。
  • 一个域名(下文用 stats.example.com),DNS 指向服务器。

第 1 步:生成密钥,写 .env

官方文档里 APP_SECRET 和 TWO_FACTOR_ENCRYPTION_KEY 都给了 openssl rand -hex 32 作为生成办法。数据库密码是我加的,用 openssl rand -hex 16 生成,只含十六进制字符,放进连接地址里不会因为特殊字符出问题(通用做法)。

bash
mkdir -p /opt/umami && cd /opt/umami
echo "POSTGRES_PASSWORD=$(openssl rand -hex 16)" >> .env
echo "APP_SECRET=$(openssl rand -hex 32)" >> .env
echo "TWO_FACTOR_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
chmod 600 .env
  • APP_SECRET:官方说明是用来保护认证令牌的随机字符串,每个安装都应该有独一无二的值。
  • TWO_FACTOR_ENCRYPTION_KEY:64 位十六进制字符串,用来加密双因素认证的密钥;设置之前没人能启用 2FA。

第 2 步:compose.yaml

下面基于官方仓库的 docker-compose.yml,改动有四处:镜像固定到具体版本;端口只绑定本机;密码和密钥改从 .env 读取;其余(健康检查、PostgreSQL 15 镜像、数据卷)与官方一致。

yaml
services:
  umami:
    image: ghcr.io/umami-software/umami:3.4.1
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      DATABASE_URL: postgresql://umami:${POSTGRES_PASSWORD}@db:5432/umami
      APP_SECRET: ${APP_SECRET}
      TWO_FACTOR_ENCRYPTION_KEY: ${TWO_FACTOR_ENCRYPTION_KEY}
    depends_on:
      db:
        condition: service_healthy
    init: true
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
      interval: 5s
      timeout: 5s
      retries: 5
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: umami
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - umami-db-data:/var/lib/postgresql/data
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 5
volumes:
  umami-db-data:

关于这几处改动:

  • 官方 Compose 里镜像写的是 ghcr.io/umami-software/umami:latest,端口是 "3000:3000"(对所有网卡开放)。官方 README 和文档的预构建镜像命令写的是 docker.umami.is/umami-software/umami:latest,是同一个项目的另一个镜像地址。我查了 GHCR 的标签列表,3.4.1 确实存在;之后升级时请先确认目标标签存在再改。
  • 官方 Compose 里 APP_SECRET 写的是占位符 replace-me-with-a-random-string,PostgreSQL 密码是 umami,直接拿去用等于用公开的值,所以我改成从 .env 读取。
  • 数据库容器没有发布任何端口,保持这样。

第 3 步:启动

bash
docker compose up -d
docker compose ps

官方说明会创建 PostgreSQL 数据库并启动 Umami,默认监听 3000 端口。第一次启动时会建表,并创建默认管理员账号。

反向代理

官方文档对反向代理只说了一句:需要把请求从你的 Web 服务器代理过来,默认应用在 http://localhost:3000。文档里最接近的 Nginx 示例是“启用 Cloudflare 头”那一页,其中的 proxy_set_header 部分可以参考。下面是我据此写的最小示例(不是官方给的完整配置,证书和细节请按自己环境调整):

nginx
server {
    listen 443 ssl;
    server_name stats.example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

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

要点:

  • 官方环境变量文档里的 CLIENT_IP_HEADER 用来指定读取访客 IP 的请求头,适合代理使用了非标准头的情况。
  • TRUSTED_PROXIES(多层代理时用)在官方文档里标注的版本是 v3.5.0,比目前最新发布的 v3.4.1 还新,所以本文不展开,用到时请先确认你的版本。
  • 想要 Cloudflare 提供的地区和城市数据,需要在 Cloudflare 里开启 “Add visitor location headers”,并让代理转发 CF-IPCountry、CF-Region-Code、CF-IPCity。官方提醒:只转发来自可信 Cloudflare 连接的这些头,并限制源站访问,防止客户端伪造。
  • 想把 Umami 放在子路径下,官方有 BASE_PATH 变量,但它是构建时变量(只在构建过程,包括自建 Docker 镜像时生效),预构建镜像用不上。最省事的办法是用独立的子域名。

第一次使用 / 把统计代码装到网站

  1. 登录并改密码。 打开 https://stats.example.com,用 admin / umami 登录。官方登录页的说明:登录后点侧边栏的个人资料按钮,进入 Settings,再到 Profile,点 Change password。
  2. (建议)开启双因素认证。 在 Settings 的 Security 标签里开启,前提是你已经配置了 TWO_FACTOR_ENCRYPTION_KEY。启用后官方会生成 10 个一次性备份码,只显示一次,要保存好;输错 5 次会锁定 15 分钟。
  3. 添加网站。 点侧边栏的 Websites,点右上角 Add website,填写名称和域名,点 Save。官方说明:名称随意,通常和域名相同;域名用来在引荐来源里过滤掉你自己网站。
  4. 拿到跟踪代码。 对要统计的网站点 Edit,在 Tracking code 区域复制代码。
  5. 贴到网站的 <head> 里。 官方给的脚本形式是下面这样(data-website-id 是你网站的编号,请复制你后台里显示的那份):
html
<script defer src="https://stats.example.com/script.js" data-website-id="你的网站ID"></script>
  1. 访问你的网站,数据会很快出现在 Umami 面板里。官方排错提示:没数据时,打开浏览器开发者工具的 Network 标签,看有没有向你的 Umami 实例发出请求、脚本有没有加载报错;广告拦截器可能会阻止脚本。Next.js 要用 next/script 组件插入;单页应用不需要额外配置,Umami 会自动统计页面切换。

跟踪脚本还能用 data- 属性配置,常用的有:

  • data-domains:只在指定域名运行(逗号分隔,按 window.location.hostname 匹配,注意带不带 www),避免预发布环境污染数据。
  • data-host-url:把数据发送到别的地址。
  • data-tag:给事件打标签,用于筛选和 A/B 测试。

更新与卸载

更新

官方文档的 Docker 更新方式是拉新镜像并重建容器:

bash
cd /opt/umami
# 把 compose.yaml 里的镜像标签改成要升级的版本(先确认标签存在)
docker compose pull
docker compose up --force-recreate -d

补充几点:

  • v3.4.1 的发布说明提到它更新了 Docker 基础镜像(固定为 Node 22.23.4-alpine,含当月 Node.js 安全修复),并说“重新构建或重新拉取镜像”才能得到。所以使用 latest 标签的话,隔一段时间也要重新拉一次。
  • 官方更新页面还提到:大版本升级(比如升到 v3)会运行数据库迁移,在大实例上可能让 PostgreSQL 的查询规划器统计信息过期,导致面板查询变慢,需要执行 ANALYZE;,官方说这个操作可以在线进行,很快完成。在本文的 Compose 里可以这样执行(用户名和库名取自上面的 Compose,这是我的改写):
bash
docker compose exec db psql -U umami -d umami -c "ANALYZE;"
  • 备份:官方更新页没有写备份命令。升级前备份 PostgreSQL 数据(比如 pg_dump,或者备份 umami-db-data 数据卷)是通用建议。

从源码安装的话,官方更新步骤是 git pull、pnpm install、pnpm build,然后重启(用 PM2 的话执行 pm2 restart umami)。

卸载

官方文档没有卸载章节。通用做法:

bash
cd /opt/umami
docker compose down        # 停止并删除容器,数据卷保留
# docker compose down -v   # 连同 umami-db-data 数据卷一起删除,统计数据会永久丢失

别忘了把网站里的 <script> 去掉。

踩坑清单

  1. 默认账号是 admin / umami,第一次登录就改。 官方在安装页和登录页都强调了这点。部署完先别急着公开域名,尽快改掉密码再对外。
  2. APP_SECRET 每个安装都要自己生成。 官方 Compose 里的 replace-me-with-a-random-string 只是占位符,用 openssl rand -hex 32 生成并保密。
  3. TWO_FACTOR_ENCRYPTION_KEY 要保管好。 官方说明:丢失或更换这个密钥,会让已保存的双因素密钥无法读取,受影响的用户需要重新绑定。
  4. v3.4.1 是安全修复版,别一直停在旧版本。 发布说明列出了依赖升级和基础镜像更新,并明确建议所有自托管用户升级。固定版本号方便回滚,但要记得定期看发布说明。
  5. 文档可能比已发布版本新。 官方环境变量文档里的 TRUSTED_PROXIES、ERRORS_PER_MINUTE 标注的版本是 v3.5.0,而最新发布的是 v3.4.1。每个变量旁边都有版本号,用之前对一下你的版本。
  6. README 和文档对 Node.js 的版本要求不一致。 README 写 Node.js 18.18+,文档站的安装页写 20.19 或更新(推荐 22 LTS)。用 Docker 部署不受影响;从源码构建的话,以文档站为准。
  7. 只支持 PostgreSQL。 官方 FAQ 和安装页都写的是 PostgreSQL(最低 v12.14)。GHCR 的标签列表里还留着 mysql-v2.x 这样的旧标签,属于 v2 时代,网上的老教程不要照抄。文档还建议把 PostgreSQL 配置为 UTC 时区,避免时间戳不一致。
  8. 官方 Compose 默认把 3000 端口开放给所有网卡。 我改成了 127.0.0.1:3000:3000。Docker 发布的端口会绕过 ufw 之类的防火墙(通用经验),数据库容器也不要发布端口。
  9. 构建时变量对预构建镜像不生效。 官方把 BASE_PATH、FORCE_SSL、DATABASE_TYPE 等列为构建时变量,只在构建(包括自建镜像)时被识别。想用 HTTP 严格传输安全头(FORCE_SSL 的作用),用预构建镜像时更现实的办法是在反向代理里加(通用做法)。
  10. 反向代理要把访客真实 IP 传给 Umami。 否则统计里记录的是代理的地址。非标准头用 CLIENT_IP_HEADER 指定。
  11. 没有 cookie,同一网络下的访客可能被合并。 官方会话文档说明,默认的会话标识由网站 ID、访客 IP、浏览器 User-Agent 和轮换的盐生成,不使用 cookie;官方在 data-distinct-id(v3.4.0)的说明里提到,办公室、学校、VPN 等共享网络下的访客可能有相同 IP 和浏览器。需要精确区分登录用户时,可以用 Distinct ID,但那是你主动发送的数据,官方说明责任在你。
  12. 默认会做遥测和版本检查。 如果你的服务器不想对外联网,用 DISABLE_TELEMETRY=1、DISABLE_UPDATES=1,要关闭所有外部请求用 PRIVATE_MODE=1(网站图标会因此不显示)。这些变量要写进 Compose 的 environment。
  13. 脚本可能被广告拦截器挡住。 官方提供 TRACKER_SCRIPT_NAME(自定义脚本名,默认是 script.js)和 COLLECT_API_ENDPOINT(自定义上报路径,默认 /api/send)来减少被拦截的可能,具体做法见官方的 “Bypass ad blockers” 页面。
  14. 数据默认永久保留。 官方 FAQ 写明自托管数据除非手动删除,否则一直保留,要留意数据库体积和磁盘。
  15. 从旧版本升到 v3 之后查询变慢,先试 ANALYZE;。 见上面的更新一节。
  16. 不要把官方 Compose 里的 postgres:15-alpine 随手改成别的大版本。 PostgreSQL 的数据目录不能跨大版本直接复用,换版本要走导出导入(通用经验,Umami 官方文档没有专门写)。

出处

想要类似的东西?网站、Telegram 机器人、运维工具,说说你的需求。
发布需求

有想法或问题?去留言板留言 →