Umami 自托管部署教程:用 Docker Compose 搭一套隐私优先的网站统计
把网站流量数据握在自己手里,是越来越多站长考虑自托管统计服务的原因。Google Analytics 功能强大,但脚本体积、Cookie 合规要求、后台数据归属等问题也让不少人心生顾虑。Umami 是一个以”简单、快速、隐私优先”为卖点的开源统计工具,自托管门槛不高,本文就用 Docker Compose 的方式,从零跑起来一个完整可用的 Umami 实例。
为什么选 Umami
Umami 的定位是 Google Analytics 的轻量替代品。它不写 Cookie、不采集个人识别信息,统计页面只跑一段极小的 JS 脚本,对前端性能几乎无感。功能上覆盖了 PV、UV、来源、页面、设备、地域等常见维度,足以应付绝大多数博客、中小站点的数据需求。
相比传统统计服务,自托管的 Umami 有两个明显好处:一是数据完全保存在自己的数据库里,二是接入非常简单——一段脚本塞进 “ 就能开工。下面进入正题。
前置准备
部署 Umami 本身对硬件要求不高,一台 512MB 内存的小机器就能跑得动,但有两点必须满足:
- Docker 与 Docker Compose:用来拉取镜像和编排容器。建议使用较新版本的 Docker Engine(具体版本以官方文档为准)。
- PostgreSQL:Umami 后端使用 PostgreSQL 存数据。如果用 Docker Compose 编排,可以一并起一个 PostgreSQL 容器;如果你已经有现成的数据库实例,直接复用也可以。
服务器操作系统 Linux 发行版都可以,Ubuntu、Debian、CentOS、Alpine 都行。准备一个工作目录,后续所有文件都放这里。
编写 docker-compose.yml
在项目目录里新建一个 docker-compose.yml,内容大致如下:
version: "3"
services:
umami:
image: docker.umami.is/umami-software/umami:latest
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://umami:umamipassword@db:5432/umami
DATABASE_TYPE: postgresql
depends_on:
- db
restart: always
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: umami
POSTGRES_PASSWORD: umamipassword
POSTGRES_DB: umami
volumes:
- umami-db-data:/var/lib/postgresql/data
restart: always
volumes:
umami-db-data:
几个关键点需要解释:
DATABASE_URL的格式必须是postgresql://用户名:密码@主机:端口/数据库名。这里db是同 Compose 网络里 PostgreSQL 服务的名称,会被 Docker 自动解析为容器 IP。- 数据库用户名、密码、库名要与
db服务里POSTGRES_*环境变量一致。 volumes命名卷umami-db-data用来持久化数据库,容器被删重建也不会丢数据。restart: always让容器在崩溃或服务器重启后自动拉起。

📷 Rubaitul Azad / Unsplash License / 来源 如果你打算让 Umami 跑在子路径(比如 `https://example.com/umami/`)而不是根域名下,还需要额外设置 `BASE_PATH` 环境变量,具体值参考官方文档说明。
启动服务
配置文件写好后,在同目录下执行:
docker compose up -d
第一次运行会拉取镜像,时间长短取决于网络。启动完成后,Umami 默认监听在容器的 3000 端口,通过宿主机的 3000 端口暴露。打开浏览器访问 http://服务器IP:3000,应该能看到登录页。
如果服务器启用了防火墙,记得放行 3000 端口(生产环境更推荐用 Nginx 反代再加 HTTPS,而不是直接暴露端口)。
首次登录与改密码
Umami 默认管理员账号是:
- 用户名:
admin - 密码:
umami
首次登录后第一件事就是改密码,并且把默认账号的用户名也一起改了。原因很简单——这个用户名和密码是公开的,互联网上有人专门扫描,没改过的实例几乎都会被登进去乱改东西。
在后台 Settings → Profile 里修改即可。如果你是给团队用,建议直接在 Settings → Users 里建新用户、分配权限,再把 admin 账号禁用或重命名。
添加网站与嵌入跟踪脚本
改完密码,就可以添加你要统计的网站了。流程分三步:
- 进入 Websites → Add website,填写网站名称和域名(域名用来过滤 referer)。
- 创建完成后,点击进入网站详情页,复制系统生成的跟踪脚本。这段脚本通常长这样:
- 把这段脚本粘到自己网站的 “ 里即可。如果是 Next.js、Nuxt 这类框架,按各自文档说明插入第三方脚本即可。
脚本加载后,访问一次对应网站,回到 Umami 后台 Realtime 页面就能看到访问数据开始流入。如果一直没数据,多半是脚本没正确插入,或者防火墙阻挡了 script.js 的请求。
配置 Nginx 反向代理与 HTTPS
直接用 IP:3000 跑生产环境既不安全也不优雅,常规做法是用 Nginx 反代到 80/443,再配合 Let’s Encrypt 上 HTTPS。
一个最小可用的反代配置大致如下:
server {
listen 80;
server_name umami.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
Umami 后台面板本身不依赖 WebSocket,所以不需要额外配 Upgrade 头。但建议在 Nginx 顶层加上 gzip 配置,对 script.js、CSS 等静态资源压缩后再传输,能进一步减小对被统计站点的影响。
HTTPS 用 certbot --nginx 一键签发即可,签完之后 Nginx 会自动重写为 443,并把 80 重定向到 443。
数据持久化与备份
Umami 的全部数据都存在那个 umami-db-data 卷里,所以最关键的运维动作就是定期备份数据库。可以用定时任务把 PostgreSQL 的 dump 拷到异地,例如每天凌晨跑一次:
docker exec umami-db pg_dump -U umami umami | gzip > /backup/umami-$(date +%F).sql.gz
保留最近 30 天或 7 份的备份,老的自动清理。备份目录建议同步到对象存储(S3、OSS 之类),防止单台服务器硬盘故障导致数据全丢。
升级 Umami 时,不要忘记的顺序是先备份再升级:
docker compose pull
docker compose up --force-recreate -d
升级后观察一下日志,确认没有报错再继续用。
常见坑与排查
部署过程中有几个高频踩坑点:
- 默认账号没改就被刷:前面已经强调过,公网部署务必第一时间改密码、改用户名。
- DATABASE_URL 格式错连不上库:密码里如果有特殊字符(
@、#、/等),要么换掉,要么用 URL 编码转义,不然 PostgreSQL 解析会出问题。 - 容器版本没对齐:
umami镜像和db镜像版本如果差太多,可能在 schema 初始化时出错。稳妥的做法是先看官方仓库的docker-compose.yml用了什么版本,照抄过来。 - 端口冲突:服务器上如果已经有别的服务占着 3000 端口,记得改
ports映射成别的宿主端口,或者直接走 Nginx 反代避开。 - 数据不增长:先看
docker logs umami里有没有报错,再用浏览器无痕窗口打开被统计页面,看 Network 里script.js是否成功加载。
下一步
跑起来之后,你可能还想深入了解 Umami 各项指标的含义、它和 Plausible、Matomo 等同类工具的对比,以及它在大流量站点下的实际表现。更详细的对比和功能测评,请期待我们后续的「Umami 评测」一文。
在那之前,先把部署跑通、数据备好,就已经完成了一个自托管统计服务最关键的 80%。


评论(0)