Rybbit Docker Compose 部署指南

Rybbit v2.9.2 Docker Compose 生产级部署实录:镜像选型、端口规划、环境变量表、数据持久化、注册建站与数据上报全链路验证,附 Origin 校验、Bot 拦截、端口冲突三大实测排障。

Rybbit自托管产品分析隐私安全2026/10/30

Rybbit Docker Compose 部署指南

目标:在一台 Linux 服务器上从零完成 Rybbit v2.9.2 的 Docker Compose 生产级部署,涵盖镜像选型、端口规划、环境变量、数据持久化、首次注册与数据上报全链路验证,并沉淀部署实测中的三处高频踩坑(Origin 校验、Bot 拦截、端口冲突)。全部命令与回显均在 Ubuntu 22.04 服务器(Docker 27 + Compose v5.6.1)实测通过,实测日期 2026-10-30。

一、环境信息

项目本次实测环境
操作系统Ubuntu 22.04(内核 5.15+)
服务器 IP192.168.1.200(局域网,无域名场景演示)
DockerDocker 27.x + Docker Compose v5.6.1
Rybbit 版本v2.9.2(2026-10-24 发布,官方最新稳定版,来源 releases.atom)
依赖组件版本ClickHouse 26.3.17.4 / PostgreSQL 17.4 / Redis 8.6.4-alpine(官方 compose 锁定)
内存/磁盘16GB 内存 / NVMe SSD(Rybbit 全家桶建议 4GB 内存起步)
端口规划Web 看板 8128 → 容器 3002;API 后端 8129 → 容器 3001;PostgreSQL 15432(宿主 5432 已被占用)
实测账号admin@example.com / Q1w2E3r4T!(注册接口实测拒绝弱口令,见 10.4)

二、镜像选型说明

官方在 v2.9.2 的 docker-compose.yml 中锁定了如下镜像组合,全部来自 GHCR/Docker Hub 官方命名空间(来源:v2.9.2 源码包 docker-compose.yml,实测 pull 全部命中):

服务镜像作用必选
clientghcr.io/rybbit-io/rybbit-client:v2.9.2Next.js 看板前端,容器端口 3002是
backendghcr.io/rybbit-io/rybbit-backend:v2.9.2Node.js API/写入/认证,容器端口 3001是
clickhouseclickhouse/clickhouse-server:26.3.17.4事件数据列式存储是
postgrespostgres:17.4用户/组织/站点等元数据是
redisredis:8.6.4-alpine会话/粘性身份/bot 计数,AOF 持久化是
caddycaddy:2.10.0内置反代与自动 HTTPS否(profiles 隔离,默认不启动)

选型要点:

  1. 必须锁版本 tag 而非 latest:backend/client 镜像发布 v2.9.2 与 latest 双 tag,本次实测 docker pull ghcr.io/rybbit-io/rybbit-backend:v2.9.2 直接命中(digest sha256:e5fc900b…)。生产升级时改 .env 中 IMAGE_TAG 即可全栈同版本切换。
  2. ClickHouse 是资源大头:官方在 compose 里已内置三份 XML config(网络监听、日志裁剪、资源上限),把 max_server_memory_usage_to_ram_ratio 限到 0.80。本次实测额外补了 mem_limit: 2560m 双保险,16GB 宿主机上 5 容器总内存占用可控。
  3. Caddy 默认不启动:官方把 caddy 服务放进 profiles: ["with-webserver"],docker compose up -d 不会拉起它。无自有反代的用户建议启用(见第十章常见问题),本文主线路径按「已有/暂无反代、先跑通数据链路」的直连模式展开。

三、前置条件

# 1. 确认 Docker 与 Compose 版本(低于 v2.23 不支持 compose 内嵌 configs 语法)
docker --version && docker compose version

实测回显:

Docker version 27.5.1, build 9f9e405
Docker Compose version v5.6.1
# 2. 规划端口:确认 8128/8129 未被占用(避开 ClickHouse 默认 8123 与常见业务端口)
ss -ltn | grep -E ':(8128|8129)\b' || echo '端口空闲'

# 3. 内存余量确认(全家桶建议可用内存 ≥4GB)
free -h | grep Mem

实测回显:

端口空闲
Mem:           15Gi       7.9Gi       2.6Gi

四、部署步骤

4.1 建目录并获取官方编排文件

Rybbit 官方不单独发布 compose 产物,编排文件随源码仓库发布。不 clone 仓库的话,直接下载官方源码包解包取文件(本文采用,实测 65MB):

mkdir -p /opt/rybbit && cd /opt/rybbit
curl -fsSL -o rybbit-2.9.2.tar.gz https://codeload.github.com/rybbit-io/rybbit/tar.gz/refs/tags/v2.9.2
tar xzf rybbit-2.9.2.tar.gz
cp rybbit-2.9.2/docker-compose.yml rybbit-2.9.2/Caddyfile .

⚠️ 网络说明:codeload.github.com 在部分网络环境直连较慢,实测可走的降级链是「直连 → 代理 http://192.168.1.62:7897」(curl -x http://192.168.1.62:7897 ...),两个渠道 2026-10-30 实测均可用。

4.2 编写 .env

在 /opt/rybbit/.env 写入(全部为本次实测值,可直接替换 IP 后使用):

# 浏览器实际访问 Rybbit 的基础地址(也是后端可信 Origin,必须与访问地址完全一致)
BASE_URL=http://192.168.1.200:8129
BETTER_AUTH_SECRET=rybbit-lab-9f3c7a1e5d2b8460-secret
DISABLE_SIGNUP=false
DISABLE_TELEMETRY=true

# 端口映射:看板 8128、API 8129(按宿主空闲端口改)
HOST_BACKEND_PORT=8129:3001
HOST_CLIENT_PORT=8128:3002

# 数据库凭据(生产务必改掉官方默认值)
CLICKHOUSE_DB=analytics
CLICKHOUSE_USER=default
CLICKHOUSE_PASSWORD=frog
POSTGRES_DB=analytics
POSTGRES_USER=frog
POSTGRES_PASSWORD=frog
REDIS_PASSWORD=rybbitRedis2026

# 镜像版本锁
IMAGE_TAG=v2.9.2

BETTER_AUTH_SECRET 生成方式(官方文档推荐):

openssl rand -hex 32

4.3 定制 compose(推荐三处)

官方 docker-compose.yml 可直接使用,但生产部署建议做三处实测验证过的定制(本节改动与实测一一对应):

  1. 显式项目名(防多套栈互相误删):
name: rybbit-2.9.2-stack
  1. 显式子网(防与其他业务网段冲突):
networks:
  default:
    name: rybbit-292-net
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.58.0/24
  1. 资源上限与端口避让:clickhouse 服务加 mem_limit: 2560m;若宿主 5432 已被占用,把 postgres 的端口映射改为 "127.0.0.1:15432:5432"。本次实测宿主 5432 被占用,直接沿用官方映射会导致 postgres 容器起不来——这是小内存多业务宿主的高频坑。

4.4 拉镜像并启动

cd /opt/rybbit
docker compose pull
docker compose up -d

docker compose up -d 实测回显(节选):

 Container backend Running
 Container client Running
 Container clickhouse Running
 Container rybbit-292-stack-postgres-1 Running
 Container redis Healthy
 Container clickhouse Healthy

启动顺序由 depends_on + healthcheck 编排保证:ClickHouse 与 Redis 健康后 backend 才启动,backend 起后 client 启动。首次启动到 backend healthy 实测约 40 秒。

4.5 部署验证

# 1. 容器状态
docker compose ps --format 'table {{.Name}}\t{{.Status}}'

实测回显:

NAME                          STATUS
backend                       Up 43 seconds (healthy)
clickhouse                    Up 49 seconds (healthy)
client                        Up 43 seconds
redis                         Up 49 seconds (healthy)
rybbit-292-stack-postgres-1   Up 49 seconds (healthy)
# 2. 后端健康检查端点
curl -s http://127.0.0.1:8129/api/health

实测回显:

OK
# 3. 看板页面与 tracker 脚本可达性
curl -s -o /dev/null -w 'client HTTP %{http_code}\n' http://127.0.0.1:8128/
curl -s -o /dev/null -w 'script.js HTTP %{http_code} | %{size_download} bytes | %{content_type}\n' http://127.0.0.1:8129/api/script.js

实测回显:

client HTTP 200
script.js HTTP 200 | 33968 bytes | application/javascript; charset=utf-8

五、数据持久化说明

官方 compose 定义了三个命名卷,宿主目录由 Docker 管理(位于 /var/lib/docker/volumes/):

卷名(前缀 rybbit-292-stack_)容器挂载点内容
clickhouse-data/var/lib/clickhouse全部页面浏览/事件/回放数据(核心资产)
postgres-data/var/lib/postgresql/data用户、组织、站点、API Key 元数据
redis-data/data会话与 bot 计数(AOF everysec)

备份与迁移建议:

# 核心数据备份(ClickHouse 原生备份方式,停写窗口极小)
docker exec clickhouse clickhouse-client --password frog \
  -q "BACKUP TABLE analytics.events TO Disk('backups', 'events-$(date +%F).zip')"

💡 技术拓展:命名卷 vs 绑定挂载——命名卷由 Docker 统一管理生命周期、可随 docker volume 工具迁移快照,是数据库类服务的推荐形态;绑定挂载(-v /opt/rybbit/data:/var/lib/clickhouse)则便于直接用 rsync 做文件级备份但要注意 UID 权限对齐。Rybbit 官方选择命名卷,自建备份脚本用 docker run --rm -v rybbit-292-stack_clickhouse-data:/data ... 挂卷导出即可,无需进入容器。

六、环境变量表

以下为 v2.9.2 官方 .env.example 与 compose 联动整理的完整变量表(「实测」列标注本次部署的实际取值与验证结果):

变量默认值说明实测
BASE_URL无(必填)浏览器访问的基址,同时是后端唯一可信 Originhttp://192.168.1.200:8129,不匹配时注册直接 403
BETTER_AUTH_SECRET无(必填)认证签名密钥,32+ 随机字符openssl rand -hex 32 生成
DISABLE_SIGNUPfalse关闭公开注册(建好管理员后建议 true)false,注册成功
DISABLE_TELEMETRY未设关闭产品遥测上报true
DOMAIN_NAME未设Caddy 模式的域名(自动 HTTPS 依赖它)未启用 Caddy,不设
HOST_BACKEND_PORT127.0.0.1:3001后端宿主端口映射8129:3001
HOST_CLIENT_PORT127.0.0.1:3002前端宿主端口映射8128:3002
CLICKHOUSE_DBanalytics事件库名analytics
CLICKHOUSE_USER / CLICKHOUSE_PASSWORDdefault / frogClickHouse 管理凭据保持默认(内网隔离)
CLICKHOUSE_QUERY_PASSWORD同上最低权限查询用户 rybbit_query 的密码未设(自动随主密码开通)
POSTGRES_DB/USER/PASSWORDanalytics / frog / frog元数据库凭据保持默认
REDIS_PASSWORDchangemeRedis 密码(compose 启动参数注入 requirepass)rybbitRedis2026
IMAGE_TAGlatestbackend/client 镜像 tagv2.9.2
MAPBOX_TOKEN空地图/地球可视化增强,可选空(看板自动降级为平面地图)
CLUSTER_WORKERS4后端 Node 集群 worker 数默认 4
LITE_DASHBOARD未设精简看板模式(低配机器建议 true)未设
DEPLOYMENT未设部署形态标识(cloud/self-host 差异化逻辑)未设
OPENROUTER_API_KEY / OPENROUTER_MODEL空AI 辅助自定义查询功能空

七、配置参数说明

三处与部署强相关的编排参数,实测口径如下:

  1. healthcheck 参数:backend 用 wget --spider http://127.0.0.1:3001/api/health,间隔 3 秒、超时 5 秒、重试 5 次、启动宽限 10 秒;ClickHouse 同款结构 ping http://localhost:8123/ping。调整 start_period 可容忍慢盘冷启动——本次实测首次启动 40 秒即 healthy,无需调整。
  2. ClickHouse 内嵌 configs:官方通过 compose configs: 段落把 4 份 XML 直接注入容器(网络监听 0.0.0.0、日志裁剪到 warning、内存上限 80% RAM、async_insert=1 + max_threads=16 profile)。这套参数无需改动即可投产;数据量上 TB 后优先调 max_memory_usage(默认 32GB 上限,小内存机器需下调)。
  3. Redis 运行参数:--requirepass $REDIS_PASSWORD --appendonly yes --appendfsync everysec --maxmemory-policy noeviction。noeviction 是刻意选择——会话键带 30 分钟 TTL,内存有界,拒绝淘汰以保证粘性身份与 bot 计数不丢。

八、配置文件挂载

官方 compose 的挂载共两类:

  1. 命名卷三类(见第五章);
  2. Caddyfile 绑定挂载:./Caddyfile:/etc/caddy/Caddyfile——仅在启用 caddy profile 时生效。启用 Caddy 模式的完整步骤:
# 1. 编辑 Caddyfile,把 {$DOMAIN} 换成你的域名
sed -i 's/{$DOMAIN}/analytics.example.com/' Caddyfile
# 2. .env 增加 BASE_URL 与域名
#    BASE_URL=https://analytics.example.com
# 3. 带 profile 启动(Caddy 会自动签发 Let's Encrypt 证书)
docker compose --profile with-webserver up -d

实测说明:本次实验宿主 80/443 已被占用且无公网域名,主线按直连模式完成全链路验证;Caddy 模式的价值在「单域名统一入口」,这也是规避第十章 Origin 踩坑的根治方案。

九、等价 docker run

理解编排等价性的单容器拆解(生产仍建议 compose;参数与 compose 定义一致):

# ClickHouse(关键点:注入官方同款网络监听与异步插入配置)
docker run -d --name clickhouse \
  --memory 2560m \
  -e CLICKHOUSE_DB=analytics -e CLICKHOUSE_USER=default -e CLICKHOUSE_PASSWORD=frog \
  -e CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 \
  -v clickhouse-data:/var/lib/clickhouse \
  clickhouse/clickhouse-server:26.3.17.4
# 后端(关键点:三个依赖地址 + BASE_URL 可信 Origin)
docker run -d --name backend \
  -p 8129:3001 \
  -e NODE_ENV=production \
  -e CLICKHOUSE_HOST=http://clickhouse:8123 \
  -e CLICKHOUSE_DB=analytics -e CLICKHOUSE_PASSWORD=frog \
  -e POSTGRES_HOST=postgres -e POSTGRES_DB=analytics \
  -e POSTGRES_USER=frog -e POSTGRES_PASSWORD=frog \
  -e REDIS_HOST=redis -e REDIS_PASSWORD=rybbitRedis2026 \
  -e BETTER_AUTH_SECRET=rybbit-lab-9f3c7a1e5d2b8460-secret \
  -e BASE_URL=http://192.168.1.200:8129 \
  -e CLUSTER_WORKERS=4 \
  ghcr.io/rybbit-io/rybbit-backend:v2.9.2
# 前端(关键点:仅依赖 backend 可达性)
docker run -d --name client \
  -p 8128:3002 \
  -e NODE_ENV=production \
  -e NEXT_PUBLIC_BACKEND_URL=http://192.168.1.200:8129 \
  ghcr.io/rybbit-io/rybbit-client:v2.9.2

十、常用运维命令与首次使用

10.1 日常运维命令

cd /opt/rybbit
docker compose ps                          # 栈健康状态
docker compose logs -f backend             # 跟随后端日志(track/查询错误都在这)
docker compose restart backend client      # 只重启应用层(数据层不动)
docker compose down                        # 停栈保留数据卷
docker compose pull && docker compose up -d   # 升级到 .env 指定的新版本

10.2 注册管理员与建站

浏览器打开 http://192.168.1.200:8128 进入看板(无头服务器可用 API 走通全流程,以下为实测命令):

# 1. 注册(Origin 头必须与 BASE_URL 一致,见 10.3 踩坑一)
curl -s -c /opt/rybbit/cookies.txt -X POST http://127.0.0.1:8129/api/auth/sign-up/email \
  -H 'Content-Type: application/json' -H 'Origin: http://192.168.1.200:8129' \
  -d '{"name":"admin","email":"admin@example.com","password":"Q1w2E3r4T!"}'

实测回显(节选):

{"token":"MADSPPrOjRclxgEG7TUfBHEoE23HwA6f","user":{"name":"admin","email":"admin@example.com",...,"id":"VtzR6oiIVkL5xQREuwAxutXfY4XUAwfd"}}
# 2. 创建组织
curl -s -b /opt/rybbit/cookies.txt -X POST http://127.0.0.1:8129/api/auth/organization/create \
  -H 'Content-Type: application/json' -H 'Origin: http://192.168.1.200:8129' \
  -d '{"name":"Demo Org","slug":"demo-org"}'

实测回显(节选):

{"name":"Demo Org","slug":"demo-org",...,"id":"lL3zKTX9RuNjYrSN1ahjXeAXBXfHiJzU","members":[{"role":"owner",...}]}
# 3. 在组织下建站(用上一步返回的组织 id)
curl -s -b /opt/rybbit/cookies.txt -X POST \
  http://127.0.0.1:8129/api/organizations/lL3zKTX9RuNjYrSN1ahjXeAXBXfHiJzU/sites \
  -H 'Content-Type: application/json' -H 'Origin: http://192.168.1.200:8129' \
  -d '{"name":"Demo Site","domain":"demo.example.com"}'

实测回显(节选):

{"id":"2c0b9eb7c6a2","siteId":1,"name":"Demo Site",...,"blockBots":true,...}

10.3 数据上报与统计验证

# 4. 上报一条页面浏览事件(site_id 用建站返回的短 id 2c0b9eb7c6a2)
curl -s -X POST http://127.0.0.1:8129/api/track \
  -H 'Content-Type: application/json' \
  -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/140.0 Safari/537.36' \
  -d '{"type":"pageview","site_id":"2c0b9eb7c6a2","hostname":"demo.example.com","pathname":"/posts/hello-1","page_title":"测试文章","screenWidth":1920,"screenHeight":1080,"language":"zh-CN","anonymous_id":"visitor-1"}'

实测回显:

{"success":true}

批量上报 5 条 pageview + 3 个访客后,用 API Key 查询汇总(Key 创建与用法详见本专栏 API 篇):

curl -s -H "Authorization: Bearer <你的APIKey>" 'http://127.0.0.1:8129/api/sites/1/overview?period=24h'

实测回显(与注入数据 3 访客/5 PV 精确吻合):

{"data":{"sessions":3,"pages_per_session":1.6666666666666667,"bounce_rate":33.33333333333333,"session_duration":0,"pageviews":5,"users":3}}

10.4 常见问题(实测排障实录)

问题一:注册/登录报 {"error":"Origin not allowed"}(403) 后端生产模式把可信 Origin 钉死为 BASE_URL(v2.9.2 server/src/lib/cors.ts 逻辑实测验证)。两种触发场景:① curl 调用时 Origin 头与 BASE_URL 不一致;② 浏览器从「看板端口 8128」跨端口请求「API 端口 8129」——API 请求会带 Origin: http://IP:8128,被后端拒绝,浏览器侧看板无法登录。根治方案是把看板与 API 收进同一域名(启用内置 Caddy,或在自有 Nginx 上把 /api 反代到 backend、其余路径反代到 client),让 BASE_URL 等于浏览器地址栏的统一入口。临时方案(纯 API 集成、无浏览器场景)是请求头带 Origin: $BASE_URL。

问题二:/api/track 返回 success 但看板数据始终为 0 先查 ClickHouse:docker exec clickhouse clickhouse-client --password frog -q "SELECT count() FROM analytics.bot_events"——实测 6 条测试事件全部躺在 bot_events。原因:新站点默认 blockBots: true,而服务端模拟请求没有浏览器端 tracker 注入的 bot 信号位,被判 bot 转存。处理:PUT /api/sites/<siteId>/config 传 {"blockBots":false} 关闭(实测回显 "message":"Site configuration updated successfully"),重发事件 6 秒内(写入队列 1 秒 flush + 异步插入合并)即出现在看板。真实浏览器流量不受此影响,生产站点无需关闭。

问题三:postgres 容器起不来,日志报端口绑定失败 宿主 5432 被其他 PostgreSQL 实例占用所致(本次实测宿主确实有 5432 监听)。把 compose 中 postgres 的映射改为 "127.0.0.1:15432:5432"。同理 ClickHouse 的 127.0.0.1:8123:8123、Redis 的 127.0.0.1:6379:6379 也可能撞车,这两者只在本机排障时有用,干脆删掉映射、仅保留容器网络内访问更安全。

问题四:弱口令注册被拒 实测 password: q1w2e3r4 无法注册(Better Auth 内置密码强度校验),改用强口令 Q1w2E3r4T! 成功。部署文档与自动化脚本中的演示账号密码需按此口径编写。

问题五:/api/sites/1/metric 返回 500 INTERNAL_ERROR 该端点除 type 外还需要 parameter 参数(如 type=pages&parameter=pathname),缺参会触发后端空指针(v2.9.2 实测 bug,已实测复现),补齐参数即返回正常数据。升级版本时应关注官方 Release Notes 是否修复。

问题六:compose up 时刷 The "MAPBOX_TOKEN" variable is not set 告警 无害告警:compose 对 .env 中未定义变量的默认空值提示,可在 .env 补 MAPBOX_TOKEN= 等空定义消除,不影响任何功能。

十一、参考链接

  1. 官方自托管文档:https://rybbit.com/docs/self-hosting
  2. v2.9.2 源码包(含 docker-compose.yml 与 .env.example):https://github.com/rybbit-io/rybbit/releases/tag/v2.9.2
  3. Releases Atom 源(版本取证):https://github.com/rybbit-io/rybbit/releases.atom
  4. 官方镜像:https://github.com/rybbit-io/rybbit/pkgs/container/rybbit-backend
  5. 本专栏选型篇:《Rybbit 介绍与选型指南》
  6. 本专栏配置篇:《Rybbit 2.9.2 配置参数详解》
  7. 本专栏 API 篇:《Rybbit 2.9.2 API 使用详解》
  8. 同类方案部署参考:《Plausible Docker Compose 部署指南》、《Aptabase Docker Compose 部署指南》