
先说背景我之前给个人博客接的是某免费统计服务数据确实不要钱但每次登录后台我都觉得别扭——访客的浏览习惯、停留时长、设备型号全躺在别人的数据库里我连导出格式都得按对方定好的来。后来决定自己本地部署一套开源网站统计工具测了几个方案长期留下的是 Plausible Analytics。这篇就完整记录我自己的过程怎么部署、怎么配反向代理实现外部访问以及每一处我踩过的坑。适合所有想让统计数据和服务器都握在自己手里的站长、开发者和运维同学。我的环境是一台 2 核 4G 的云主机系统是 Ubuntu 22.04主站在另一个域名上统计工具单独用一个子域名访问。整体思路很简单服务器上跑一套 Docker Compose把 Plausible 的容器和数据库拉起来然后把一个子域名指过去通过反向代理加 HTTPS 对外提供服务。下面从选型开始讲因为很多人第一步就纠结住了。1. 为什么是 Plausible自托管统计工具的选择逻辑1.1 自托管想解决的真实问题先说动机。网站统计这件事看起来只是“看几个数字”但真往深了想会发现它牵涉三个问题数据归属、隐私合规、成本。数据归属最关键。用第三方统计服务你每天看报表可底层数据全在对方库里。对方改版、调整政策、甚至停止服务你毫无办法。自托管以后数据库文件就在自己服务器上想导什么导什么想存多久存多久。隐私合规是第二层。现在访客对浏览器指纹、跨站追踪越来越敏感你也不希望自己的站点因为挂了个重型统计脚本而被各种广告拦截器直接屏蔽。Plausible 的脚本非常轻只有一个 JS 文件不设置跨站 Cookie不用指纹识别天然符合当前主流隐私思路。成本则是现实问题。Plausible 官方 SaaS 版本按站点数量收费小站点还好站点多了是一笔持续支出。自托管没有授权费你要付出的只是一台服务器和一点维护时间。这也是我最终选择它的直接原因。1.2 和主流替代方案的直接对比市面上自托管的开源统计工具其实不少我认真比过 Umami、Matomo、Plausible 三款也简单试过 GoAccess 这类日志分析方案。下面这个表是我的使用感受不是官方参数对比偏实操项目定位技术栈数据存储上手难度适合场景Plausible轻量、隐私友好Elixir ClickHouse PostgreSQL事件表用 ClickHouse配置数据用 PostgreSQL中看重简洁和隐私希望脚本极小的站点Umami轻量、灵活Node.js PostgreSQL/MySQL单库存储低想快速自托管界面清爽二次开发需要Matomo功能全面PHP MySQL单库存储中需要完整用户画像、漏斗分析、电商报表GoAccess日志解析C实时解析日志低有服务器日志不需要 JS 埋点离线分析Plausible 最打动我的一点是它把事情做“少”了。它不去尝试覆盖 GA 的全部功能而是专注在“浏览量、来源、访客设备、热门页面、实时在线”这些核心指标上界面干净报表生成也快。对于内容站、个人博客、独立开发者产品页来说这些指标完全够用还能少操心很多。Matomo 功能确实最强但同样带来了更重的数据库、更复杂的配置和更高的小白学习门槛。Umami 上手轻松Node 生态也熟悉但我自己用下来长时间运行时的内存管理和数据聚合效率不如 Plausible维护频率更高一点。1.3 什么人适合这套部署如果你符合下面任意一条我觉得 Plausible 自托管很适合手里有一台配置不算太高的服务器想榨干它的剩余价值运营个人博客、文档站、独立产品官网统计需求是“看得清楚”而不是“分析到极致”不想再让第三方知道你的访客行为数据对 Elixir、ClickHouse 这些技术栈有兴趣想借部署了解一下现代数据组件怎么协作。反过来如果你需要做电商漏斗、用户分群、深度自定义报表那还是老老实实用 Matomo或者直接用商业统计工具别在自托管上花时间。2. 部署前的架构与资源规划先弄懂三个容器在干什么2.1 拆解 Plausible 的运行链路很多教程上来就让你跑 docker compose但我建议先花五分钟搞清楚这套东西由什么组成。Plausible 自托管版官方给的 Docker Compose 一般包含三到四个服务plausible主程序容器负责 Web 界面、API、统计脚本分发、报表生成plausible_dbPostgreSQL 数据库存用户账号、站点配置、报表设置等关系型数据plausible_events_dbClickHouse 数据库存访客事件流也就是真正要分析的原始数据reverse-proxy官方模板里的 Caddy 容器用来处理 HTTPS 和反向代理。我当时第一次看到有两个数据库也愣了下。后来想明白了PostgreSQL 管的是“你怎么配置”ClickHouse 管的是“访客干了什么”。这是两种完全不同的数据模型。PostgreSQL 是传统关系型适合频繁更新、强一致性的结构化数据比如“我的站点域名是什么”“我开启了什么功能”“我的账号密码和两步验证”。ClickHouse 则是列式分析数据库专门为海量事件写入和聚合查询设计。统计脚本每来一个访客行为就朝 ClickHouse 里写一条事件你打开 Dashboard 看趋势图时ClickHouse 在极短时间内完成聚合返回结果。如果用 PostgreSQL 硬扛事件表数据量一上去查询就慢得没法看。2.2 服务器配置要求与我的实测建议官方文档对生产环境的内存建议一直是 2GB 起步这个数字我实测过是靠谱的。原因在于 ClickHouse 非常吃内存尤其容器方式部署时默认配置下启动阶段就可能占掉 1GB 以上。如果你拿 1G 内存的小鸡硬跑大概率遇到 ClickHouse 被系统 OOM Killer 杀掉表现就是容器反复重启、登录页打不开。我自己的使用场景是个人博客加两个小产品站日均 PV 在几千这个量级2 核 4G 的服务器跑得很轻松。如果你预计站点流量很大内存加到 4G 以上更稳CPU 倒不是瓶颈因为 Plausible 的实时报表并不需要持续做重型计算。存储方面Plausible 的写放大不夸张但 ClickHouse 默认会保留一定时间的历史数据。我建议系统盘至少留出 20-30GB 余量并且把 Docker 数据目录单独放到数据盘上别和自己的备份文件混在一个分区。2.3 域名、端口和公网访问的整体规划在动手部署之前先把外面访问你统计服务的路径想清楚。我的规划是这样用一个单独的子域名比如stats.example.com专门给 Plausible 用不要让 Plausible 的容器端口 8000 直接暴露到公网而是只监听在服务器本机由服务器上的反向代理我前面说了 Caddy 方案监听 80/443把对应域名的请求转发给本机的 Plausible 端口云服务商的安全组、防火墙里只放行 80 和 443。这样做的好处是你后续想在同一台服务器上挂别的 Web 服务也可以共用同一个反向代理入口不会出现端口冲突。Plausible 本身其实能直接监听 80但那样既不方便统一管理 TLS 证书也容易和其他应用打架。域名这块提前在 DNS 管理后台加一条 A 记录把子域名指向服务器公网 IP 即可。如果你有 CDN 习惯建议先把直连调通再加 CDN否则排查问题会多一层干扰。3. 用 Docker Compose 拉起整套服务配置逐项说明3.1 生成两个必须的密钥我第一次部署时没太在意密钥结果后面反复被登出、登录失败才明白这两个值有多关键。第一个是SECRET_KEY_BASE。它用于加密 Plausible 的登录会话 Cookie。这个值如果随便填个短字符串系统能跑但安全性很差更麻烦的是如果你以后重建容器时换了它所有已登录用户的会话都会失效老用户会被强制要求重新登录一次。第二个是TOTP_VAULT_SECRET。这是较新版本的 Plausible 新增的密钥用来加密存储两步验证2FA的密钥。没设这个值时老版本还能启动但新版本可能直接引导你去初始化。我建议一开始就生成好避免后续开 2FA 时踩坑。生成方法很简单Linux 上直接openssl rand -base64 64把输出结果完整复制保存一条给 SECRET_KEY_BASE一条给 TOTP_VAULT_SECRET。注意不要用自定义的可读字符串随机性很重要。3.2 补齐关键环境变量我用的是官方提供的 Docker Compose 模板但实际部署时改了不少环境变量。完整的官方模板建议直接从官方 GitHub 仓库拉最新版本我这里把几个最关键的点列出来方便你对照检查BASE_URLhttps://stats.example.com SECRET_KEY_BASE上面的随机串 TOTP_VAULT_SECRET另一个随机串 DATABASE_URLpostgres://plausible:plausibleplausible_db:5432/plausible CLICKHOUSE_DATABASE_URLhttp://plausible_events_db:8123/plausible DISABLE_REGISTRATIONfalse PORT8000逐个说BASE_URL是最容易出错的配置。它写的是“这个系统对外访问的完整地址”协议、域名都必须和最终用户访问时一致。我刚开始填了http://127.0.0.1:8000后来从外网访问时就发现生成的脚本地址、登录回调地址全是127.0.0.1自然全部失败。正确做法是直接填你规划好的完整地址比如https://stats.example.com。DISABLE_REGISTRATION是注册开关。第一次启动时必须设成false才能访问注册页创建管理员账号。等账号建完我会把它改成true并重启防止陌生人访问你的实例注册账号。PORT是主程序容器内部监听的端口默认8000后面反向代理转发就是指向这个端口。它与连接网址里的端口不是一回事别搞混。3.3 启动容器并处理首启异常在配置好.env文件后参考官方 compose 文件把服务都列好然后docker compose up -d第一次启动最常遇到的问题就是 ClickHouse 起不来。检查方式docker compose logs plausible_events_db如果看到内存不足的报错或者容器自动退出优先把服务器内存升级到 2GB 以上或者在 ClickHouse 的启动参数里限制内存。我自己第一次用 1G 内存的机器跑就是在这里卡了好一阵后来换了 4G 内存的服务器一次通过。另一个常见问题是容器起来了但主程序一直连不上数据库。这时看看plausible容器日志基本都是DATABASE_URL写错或者数据库容器还没完全初始化完毕。Compose 里的健康检查配置不能省等数据库 ready 再启动主服务会稳很多。正常启动后可以先用curl -I http://127.0.0.1:8000在本机测试看到返回 200 就说明内部服务已经通了。3.4 注册管理员账号并关闭开放注册服务起来后用浏览器访问临时地址比如先通过 SSH 隧道或临时放开端口访问进入/register页面注册管理员账号。这里有个细节注册页面出现的前提是DISABLE_REGISTRATIONfalse否则会直接 403。注册成功后先别急着关闭注册建议在这个账号里把站点配置好、做完基础设置再改环境变量。关闭注册的操作是# 修改 .env 文件 DISABLE_REGISTRATIONtrue # 重启容器 docker compose up -d改完后外部用户再访问/register会收到 403而你自己已经登录的会话不会受影响。4. 外部访问的最后一公里反向代理与 HTTPS4.1 先想明白外部访问到底要走哪条路当你本机能正常打开 Plausible 登录页之后剩下的核心工作就是“让外部访问能到这台服务器的这个端口”。这一步技术含量不高但非常容易出幺蛾子。如果你用的是云主机第一步是确认防火墙和安全组都放行了 80 和 443并且确认 Plausible 容器没有直接绑定宿主机公网端口的映射。我见过有人图省事把8000:8000直接映射出来结果后续配 HTTPS 时Caddy 和 Plausible 同时监听 80冲突。正确做法是内部容器用 Docker 网络互连只有反向代理容器把 80/443 映射到宿主机。4.2 Caddy 方案自动 HTTPS最省心官方模板内置了 Caddy 作为反向代理我强烈建议新手直接用它。原因只有一个Caddy 能自动申请和续期 Lets Encrypt 证书你不需要手动处理证书文件。一个最简 Caddyfile 配置如下stats.example.com { reverse_proxy plausible:8000 }Caddy 会自动完成 ACME 协议申请证书、绑定 443 端口、把请求转发到 compose 网络里的plausible容器。你只需要确保域名 A 记录确实指向了服务器公网 IP。如果 Caddy 容器不是和 Plausible 在同一个 compose 网络把plausible:8000改成127.0.0.1:8000也行但前提是 Plausible 容器端口已经映射到宿主机本机。我推荐维持同一个 compose 网络少暴露一层端口。4.3 Nginx 方案传统服务器运维的选择如果你服务器上已经跑着 Nginx不想再加一个 Caddy 容器也可以用 Nginx 做前端代理。下面是我实际用过的配置重点看请求头处理server { listen 80; server_name stats.example.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; } }TLS 证书部分可以用 certbot 等工具签发配置基本和普通站点一致。这里最关键的坑是X-Forwarded-For如果你不把访客真实 IP 传进去Plausible 统计到的所有访客来源都会显示成127.0.0.1地域报表和来源报表直接全废。我一开始就漏了这个上线好几天后才发现访客 IP 全是本机地址。还有一点proxy_set_header Host $host必须保留。Plausible 会通过 Host 头判断当前访问域名是否和BASE_URL匹配你如果漏配了这个头会触发 400 Bad Request。4.4 常见的反代配置错误把我在社区里见过的几种典型错误汇总一下你排查时可以对照错误现象常见原因解决办法访问域名返回 400Host 头未传递或 BASE_URL 与访问域名不一致检查 Nginx 的 proxy_set_header Host检查 BASE_URL 是否带协议和正确域名页面能打开但脚本地址是 httpBASE_URL 没写 https修改 BASE_URL 为完整 https 地址重启容器登录后跳回登录页SECRET_KEY_BASE 变化导致会话失效固定 SECRET_KEY_BASE恢复之前生成的随机串统计来源全部是本机 IP反代未传 X-Forwarded-For补上 X-Forwarded-For 请求头并重启 NginxHTTPS 证书一直申请失败域名解析未生效或 80 端口被本地服务占用确认 DNS A 记录确认服务器 80 端口只被 Caddy/Nginx 监听我自己的经验是把所有容器日志打开docker compose logs -f常驻一个窗口当页面报错时立刻按时间线倒推大部分问题都是配置头的缺失或 BASE_URL 不一致。5. 把统计脚本接到站点上从嵌入到验证数据入库5.1 在后台添加站点并获取脚本外部访问链路打通后回到 Plausible 后台左侧菜单进入“Sites”点击添加新站点。这里填的域名必须和你要监控的站点域名一致比如example.com。保存后页面会给出一段脚本script defer>script defer># 备份 PostgreSQL docker exec postgres容器名 pg_dump -U plausible plausible /backup/plausible-db.sql # 备份 ClickHouse 数据目录需要停容器或使用快照 cp -a /var/lib/docker/volumes/clickhouse卷名/_data /backup/clickhouse-eventsClickHouse 的数据一致性要求比较高最简单可靠的备份是直接对数据目录做快照或者停掉容器再拷贝。个人站点流量不大停一分钟无所谓流量大的场景建议用 ClickHouse 官方备份工具别直接拷贝运行中的数据文件。恢复时先恢复 PostgreSQL再恢复 ClickHouse 数据目录最后启动容器。我把这套写成了两个 shell 脚本半小时能完成一次全量恢复演练。这个步骤一定要提前试一次等数据丢了再学就晚了。6.3 升级流程Plausible 的开发节奏不算快但每次升级我都不跳过。官方升级流程很简单docker compose pull docker compose up -d升级前我会做两件事先备份数据库再看官方 Changelog确认没有破坏性变更和需要手动执行的操作。有一两次新版本改了环境变量的默认值升级后容器启动失败都是靠备份和日志回滚解决的。我的习惯是升级后一周内不删旧镜像docker images里保留前一个版本确认新版本稳定运行后再清理。6.4 我踩过的三个坑最后分享三个我实打实踩过的坑希望你看到时少走弯路。第一个是 ClickHouse 内存不足被 OOM 杀掉。当时我用一台 1G 内存的机器部署容器反复重启日志里全是内存相关错误。折腾了半天才发现是 ClickHouse 需要的内存远超整个服务器可用内存。后来换到 4G 机器才稳定。如果你只有 1G 内存建议直接放弃 Plausible选 Umami 这类更轻的方案。第二个是BASE_URL和反代配置都正常但统计到的来源地域全是“本地”。排查到最后发现是 Nginx 层没传X-Forwarded-For。这个请求头值的设置非常隐蔽不看你不会想到是它。第三个是升级时 ClickHouse 卷的数据权限问题。有次升级后容器以不同 UID 启动导致 ClickHouse 数据目录不可写服务直接起不来。解决方法是确认镜像启动用户和数据目录的所有者一致必要时把数据目录 chown 给指定用户。这套东西我已经稳定跑了快一年期间只做过几次例行升级没有再为统计工具操过心。如果你也想摆脱外部统计依赖把数据真正攥在自己手里照着上面的链路走一遍大多数问题都能提前绕开。最后一点小建议域名和 HTTPS 一定要在第一天就配好不要用 IP 先跑着“以后再说”后面改 BASE_URL 引发的连锁问题会让你怀疑人生。