Stoat 0.15.5 Docker Compose 部署指南
用官方 self-hosted 编排从零把一套完整的 Stoat 0.15.5 自托管聊天平台跑起来,覆盖配置生成、全量拉起、入口验证、注册建服发消息的核心链路验证与数据持久化说明。已在 2C8G 的 Ubuntu 22.04 环境实测通过(入口 `http://192.168.1.200:8140`,注册/登录/建服/发消息/附件上传全链路验证可用)。
Stoat 0.15.5 Docker Compose 部署指南
目标:用官方 self-hosted 编排从零把一套完整的 Stoat 0.15.5 自托管聊天平台跑起来,覆盖配置生成、全量拉起、入口验证、注册建服发消息的核心链路验证与数据持久化说明。已在 2C8G 的 Ubuntu 22.04 环境实测通过(入口
http://192.168.1.200:8140,注册/登录/建服/发消息/附件上传全链路验证可用)。
一、环境信息
本篇全部命令与回显来自以下实测环境,读者复现时请把服务器 IP 按实际情况替换:
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 22.04(内核 7.0.0) |
| 架构 | x86_64 |
| Docker / Compose | Docker 24+ / Compose v2(docker compose 子命令形态) |
| Stoat 版本 | v0.15.5(ghcr.io/stoatchat 服务端全家桶) |
| Web 前端 | for-web(镜像 tag 4017c18,对应 v0.15.3 代际) |
| 对象存储 | pgsty/silo(S3 兼容,MinIO 协议) |
| 数据库 | MongoDB 7(官方编排默认 mongo:8,见「十一、常见问题」兼容性说明) |
| 入口地址 | http://192.168.1.200:8140(实验环境 80/443 端口受限,经 compose.override.yml 把 Caddy 映射到 8140) |
| 数据目录 | /opt/stoat/data/(compose 项目目录下的 data/ 子树) |
| 部署目录 | /opt/stoat/(self-hosted 仓库克隆目录) |
⚠️ 说明:官方标准部署使用域名 + 80/443 端口并自动签发 HTTPS 证书。本实验环境 80/443 已被其他服务占用,因此入口降级为
IP:8140纯 HTTP,「四、部署步骤」第八步单独给出这一场景的适配实录;有域名的生产部署直接按主线走即可。
二、镜像选型说明
Stoat 官方把自托管编排与后端服务分仓维护:stoatchat/self-hosted 仓库提供 compose.yml,一次编排拉起 16 个服务。版本口径的铁律是以 self-hosted 仓库 compose.yml 里的镜像标签为准,而不是后端 monorepo 的最新 release——本次部署时后端已发布到 v0.16.2,但编排仓库 main 分支联调钉住的仍是 v0.15.5。全量镜像清单(官方 compose.yml 逐行核读 + 部署后 docker compose ps 实测):
| 服务 | 官方镜像 | 角色 |
|---|---|---|
| api | ghcr.io/stoatchat/api:v0.15.5 | REST API(delta),全部业务入口 |
| events | ghcr.io/stoatchat/events:v0.15.5 | WebSocket 实时事件(bonfire) |
| autumn | ghcr.io/stoatchat/file-server:v0.15.5 | 文件上传下载服务 |
| january | ghcr.io/stoatchat/proxy:v0.15.5 | 链接预览与图片代理 |
| gifbox | ghcr.io/stoatchat/gifbox:v0.15.5 | Tenor GIF 代理 |
| crond | ghcr.io/stoatchat/crond:v0.15.5 | 定时任务守护进程 |
| pushd | ghcr.io/stoatchat/pushd:v0.15.5 | 推送通知守护进程 |
| voice-ingress | ghcr.io/stoatchat/voice-ingress:v0.15.5 | 语音信令入口 |
| livekit | ghcr.io/stoatchat/livekit-server:v1.9.14 | 音视频 SFU |
| web | ghcr.io/stoatchat/for-web:4017c18 | Web 前端 |
| caddy | docker.io/caddy | 统一入口反代(按路径分发) |
| database | docker.io/mongo:8(官方)/ mongo:7(实测降级) | 业务库 |
| redis | valkey/valkey:9-alpine | 事件总线与 KV 缓存 |
| rabbit | docker.io/rabbitmq:4 | 内部队列 |
| minio | docker.io/pgsty/silo | S3 兼容对象存储 |
| createbuckets | docker.io/pgsty/mc | 一次性建桶任务 |
💡 技术拓展:自托管 IM 的「编排形态」常见三种取向——
形态 代表 特点 官方全家桶编排 Stoat/Revolt(16 服务单 compose) 服务拆细、各司其职,升级按镜像标签整体走 单体服务 + 外挂数据库 Mattermost 组件少、运维简单,扩展性靠单体自身 Helm/K8s 化编排 Rocket.Chat 社区 Chart 适合已有 K8s 平台的团队 Stoat 官方只维护 compose 形态,官方文档未提供 Helm Chart;本文采用官方全家桶编排,理由是版本组合经过官方联调,自行拆装任意子系统容易踩到配置漂移。
关于 database 镜像的一个实测结论:官方编排引用 docker.io/mongo:8,但在本实验环境(内核 7.0.0)该版本服务无法启动,把镜像改为 docker.io/mongo:7 后一次通过健康检查,且 0.15.5 全部功能链路在 MongoDB 7 上验证可用。MongoDB 对 CPU 指令集与内核特性有平台要求,遇到同类「容器反复重启、日志出现非法指令/启动崩溃」时,可先降级 mongo:7 验证。Redis 侧官方用 Valkey 9(Redis 协议兼容),README 同时提供换回 valkey:8 的兼容说明。
三、前置条件
- 硬件:官方建议至少 2 vCPU、2 GB 内存(README Deployment 一节);实测 2C8G 下 16 个服务常驻内存约 2.5 GB,含语音视频使用建议 4 GB 起步。
- 软件:已安装 Docker Engine 与 Compose v2 插件,
docker compose version能正常输出版本号。 - 网络与域名(生产标准形态):
- 域名 A/AAAA 记录指向服务器;
- 防火墙放行:80/TCP 与 443/TCP(Web/API 入口)、7881/TCP 与 50000-50100/UDP(LiveKit 语音 RTC 直连端口,这两个不走 Caddy);
- 官方 README 给出 ufw 放行示例:
ufw allow 80,443/tcp、ufw allow 7881/tcp、ufw allow 50000:50100/udp。
- 依赖工具:
git、openssl(配置生成脚本要用来产密钥)、chmod权限正常即可。
四、部署步骤
本章按官方标准流程走完 0→1 部署,并在最后给出核心链路的验证回显。全程唯一的外部下载是 git 克隆编排仓库与拉取镜像。配置文件生成产物(Revolt.toml/secrets.env/livekit.yml 等)的逐项含义见 《Stoat 0.15.5 配置参数详解》。
4.1 克隆官方编排仓库
git clone https://github.com/stoatchat/self-hosted stoat # 拉取官方自托管编排到 /opt/stoat
cd stoat # 进入编排目录
克隆完成后目录内应包含 compose.yml、generate_config.sh、secrets.env.example、Guides.md 等文件:
ls
实测输出(节选):
Caddyfile Guides.md LICENSE README.md compose.yml generate_config.sh livekit.yml migrations secrets.env.example
4.2 生成配置与密钥(generate_config.sh)
官方推荐的配置生成只有一条命令,把 your.domain 换成你的实际域名:
chmod +x ./generate_config.sh # 赋予配置脚本执行权限
./generate_config.sh your.domain
脚本会依次做五件事(generate_config.sh 逐行核读确认):
- 询问「是否置于其他反向代理之后」(
Would you like to place Stoat behind another reverse proxy? [y/N])。答N(默认)→ Caddy 作为主入口,占用 80/443,并把域名写入.env的STOAT_DOMAIN;答y→ 认为你前面还有一层反代,Caddy 改听 8880 端口并生成compose.override.yml。 - 询问「是否启用摄像头与屏幕共享」(
Would you like to enable camera and screen sharing? [Y/n])。答Y(默认)→ 在Revolt.toml里写入 1080p 视频上限(video_resolution = [1920, 1080])。 - 生成三组密钥写入
secrets.env:VAPID 推送密钥对(openssl ecparam生成 EC 私钥再转公钥)、文件加密密钥(openssl rand -base64 32)、LiveKit key/secret(openssl rand -hex)。 - 生成服务配置
Revolt.toml:hosts 段全部钉在https://<你的域名>(api/events/autumn/january/gifbox 五个服务 URL + LiveKit 节点)。 - 生成
livekit.yml(RTC 端口与密钥)、前端环境.env.web、客户端发现配置stoat.json。
⚠️ 注意:脚本的防覆盖保护很严格——目录里已存在
Revolt.toml时会拒绝执行,必须显式加--overwrite且存在secrets.env才允许覆盖。这是为了防止把旧实例的加密密钥刷掉导致已上传文件永久不可读。确需覆盖时:./generate_config.sh --overwrite your.domain。
4.3 全量拉起
docker compose up -d
首次执行会拉取 16 个镜像(约 1.5 GB),随后按依赖顺序启动:database/rabbit 的健康检查通过后,api/events 等应用服务才会启动(compose.yml 用 condition: service_healthy 控制)。
4.4 状态验证
docker compose ps
实测输出(节选,STATUS 列关键字段):
NAME STATUS
stoat-api-1 Up
stoat-autumn-1 Up
stoat-caddy-1 Up
stoat-createbuckets-1 Exited (0)
stoat-crond-1 Up
stoat-database-1 Up (healthy)
stoat-events-1 Up
stoat-gifbox-1 Up
stoat-january-1 Up
stoat-livekit-1 Up
stoat-minio-1 Up
stoat-pushd-1 Up
stoat-rabbit-1 Up (healthy)
stoat-redis-1 Up
stoat-voice-ingress-1 Up
stoat-web-1 Up
逐项解读:database 与 rabbit 显示 healthy(healthcheck 生效);createbuckets 显示 Exited (0) 是正常状态——它是一次性建桶任务,把 revolt-uploads 桶建完就退出,退出码 0 表示成功,不要试图去"修复"它。
4.5 入口验证
curl -s -o /dev/null -w '%{http_code}\n' http://localhost/
实测输出(标准部署端口下):
200
再验证 API 根端点的版本自报:
curl -s http://localhost/api/ | head -c 200
实测输出(节选):
{"revolt":"0.15.5","features":{"captcha":{"enabled":false,"key":""},"email":false,"invite_only":false,"autumn":{"enabled":true,...
"revolt":"0.15.5" 即服务端版本(Stoat 是 Revolt 的社区延续,API 自报字段沿用 revolt 名称);email:false 表示未配置 SMTP 邮件验证,invite_only:false 表示开放注册。
4.6 注册与登录验证
浏览器打开 http://你的域名(或 http://服务器IP)即可看到 Web 客户端注册页。命令行方式验证核心链路(密码不能使用弱口令,原因见「十一、常见问题」):
curl -s -X POST http://localhost/api/auth/account/create \
-H 'Content-Type: application/json' \
-d '{"email":"root@stoat.local","password":"q1w2e3r4T!"}'
实测输出:成功时返回空响应体(HTTP 204);随后登录:
curl -s -X POST http://localhost/api/auth/session/login \
-H 'Content-Type: application/json' \
-d '{"email":"root@stoat.local","password":"q1w2e3r4T!","friendly_name":"cli"}'
实测输出(节选):
{"result": "Success", "_id": "01M658ARB9BY1A881X0ED94C13", "user_id": "01M65871VK4Y840R0RMN9X9Z38", "token": "TYcaWBdo4x_GKHNpzjwrVgqqlNVSgizglvUBkKBcG9mt2G0Arhsyol9MpNwOfCvj", ...
拿到 token 后即可调用带认证的接口。建服务器、发消息等完整 API 链路见 《Stoat 0.15.5 API 使用详解》。
4.7 语音端口直连验证(可选,启用音视频时)
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:7881/
实测输出:
200
7881 是 LiveKit 的 RTC TCP 端口,必须能从公网/客户端网络直连(不经过 Caddy);UDP 50000-50100 同理。
4.8 无域名 / 端口受限场景适配实录(IP:PORT 纯 HTTP)
本实验环境 80/443 端口被占用,无法走官方默认的「域名 + 自动 HTTPS」形态,适配实录如下(实测文件全部来自 /opt/stoat/):
第一步:用 compose.override.yml 把 Caddy 入口映射到 8140(Compose 会自动叠加该文件):
services:
caddy:
ports: !override
- "8140:80"
!override 是 Compose 的覆盖标签,表示替换(而非追加)compose.yml 里 caddy 的 ports 列表,从而去掉 443 映射。
第二步:重跑 ./generate_config.sh <参数> 后,把生成文件里的域名统一手工替换为 http://192.168.1.200:8140(纯 HTTP + IP + 端口)。四个文件要保持口径一致:
Revolt.toml 的 hosts 段(实测值):
[hosts]
app = "http://192.168.1.200:8140"
api = "http://192.168.1.200:8140/api"
events = "ws://192.168.1.200:8140/ws"
autumn = "http://192.168.1.200:8140/autumn"
january = "http://192.168.1.200:8140/january"
gifbox = "http://192.168.1.200:8140/gifbox"
[hosts.livekit]
worldwide = "ws://192.168.1.200:8140/livekit"
stoat.json(客户端发现配置,实测值):
{"api":"http://192.168.1.200:8140/api"}
.env.web(前端构建环境变量,实测值):
HOSTNAME=:80
VITE_HOST=stoat.example.local
VITE_API_URL=https://stoat.example.local/api
VITE_DEV_GIFBOX_URL=https://stoat.example.local/gifbox
.env(compose 域名变量,实测值):
STOAT_DOMAIN=
第三步:重启生效并验证:
docker compose up -d # 配置文件是 bind mount,改完重启相关服务即可
验证点:curl -s http://localhost:8140/api/ 返回的 JSON 中 autumn.url、january.url、ws、app 字段应全部是 http://192.168.1.200:8140/... 形态。实测回显:
"autumn":{"enabled":true,"url":"http://192.168.1.200:8140/autumn"},"january":{"enabled":true,"url":"http://192.168.1.200:8140/january"},...
"ws":"ws://192.168.1.200:8140/ws","app":"http://192.168.1.200:8140"
⚠️ 注意:hosts URL 改了但服务端各组件没有同步时,会出现「页面能打开、上传/预览报错」的典型错配——所有客户端直连的 URL 都来自 Revolt.toml 的 hosts 段,必须整套一起改。
五、数据持久化说明
官方编排把全部持久化数据收敛在编排目录下的 data/ 子树(bind mount 到各服务),备份 = 备份这个目录 + 密钥文件:
| 宿主路径 | 服务 | 内容 |
|---|---|---|
./data/db | database | MongoDB 业务库(用户/消息/服务器全部业务数据) |
./data/minio | minio | S3 对象存储(头像/附件/表情等全部上传文件) |
./data/rabbit | rabbit | RabbitMQ 队列数据 |
./data/caddy-data | caddy | TLS 证书与站点数据 |
./data/caddy-config | caddy | Caddy 动态配置 |
./secrets.env | (文件) | 加密密钥与推送密钥(不是目录) |
./Revolt.toml | (文件) | 服务配置 |
⚠️ 注意:
secrets.env里的文件加密密钥(REVOLT__FILES__ENCRYPTION_KEY)是上传文件可读性的唯一凭据——丢失后 MinIO 里所有存量文件将永久无法解密。官方 README 反复强调备份它,且generate_config.sh不带--overwrite永远不会重生成已存在的密钥。日常备份建议:data/目录 +secrets.env+Revolt.toml一起打包,密钥文件单独加密存放。
六、环境变量表
Stoat 的部署期变量分三层:compose 项目变量、前端构建变量、服务密钥。逐层列出(名称 → 作用 → 实测值/取值方式):
.env(compose 项目变量)
| 变量 | 作用 | 取值 |
|---|---|---|
STOAT_DOMAIN | Caddy 网络别名的域名占位(caddy 服务的 network alias) | 标准部署填你的域名;反代模式留空 |
.env.web(Web 前端构建变量,由 generate_config.sh 生成)
| 变量 | 作用 | 取值 |
|---|---|---|
HOSTNAME | web 服务监听地址 | 标准部署 https://你的域名;反代/降级模式 :80 |
VITE_HOST | 前端自报主机名 | 你的域名 |
VITE_API_URL | 前端调 API 的基准地址 | https://你的域名/api |
VITE_DEV_GIFBOX_URL | GIF 代理地址 | https://你的域名/gifbox |
secrets.env(服务密钥,由 generate_config.sh 自动生成,格式 REVOLT__<段>__<键> 双下划线映射到 Revolt.toml 的配置路径)
| 变量 | 映射到 Revolt.toml | 作用 |
|---|---|---|
REVOLT__PUSHD__VAPID__PRIVATE_KEY | [pushd.vapid] private_key | Web Push 推送的 VAPID 私钥 |
REVOLT__PUSHD__VAPID__PUBLIC_KEY | [pushd.vapid] public_key | VAPID 公钥(浏览器订阅推送用) |
REVOLT__FILES__ENCRYPTION_KEY | [files] encryption_key | 上传文件的服务端加密密钥(丢失=文件不可读) |
REVOLT__API__LIVEKIT__NODES__WORLDWIDE__KEY | [api.livekit.nodes.worldwide] key | LiveKit 节点接入 key |
REVOLT__API__LIVEKIT__NODES__WORLDWIDE__SECRET | [api.livekit.nodes.worldwide] secret | LiveKit 节点接入 secret |
compose.yml 内固定写死的环境变量(官方默认值,可按需修改):
| 变量 | 所属服务 | 默认值 | 说明 |
|---|---|---|---|
RABBITMQ_DEFAULT_USER / RABBITMQ_DEFAULT_PASS | rabbit | rabbituser / rabbitpass | 与 Revolt.toml [rabbit] 段保持一致,改要两边同步 |
MINIO_ROOT_USER / MINIO_ROOT_PASSWORD | minio | minioautumn / minioautumn | 与 Revolt.toml [files.s3] 的 access key 一致,改要两边同步 |
MINIO_DOMAIN | minio | minio | S3 虚拟主机域名 |
⚠️ 注意:
rabbituser/rabbitpass、minioautumn/minioautumn是官方编排的公开默认凭据,生产部署应修改——RabbitMQ 的改 compose.yml 环境变量,MinIO 的还要同步 Revolt.toml[files.s3]的access_key_id/secret_access_key,否则文件服务连不上存储。
七、配置参数说明
部署期需要关心的配置集中在两个文件,其余参数(注册安全、上传限额、推送通道、功能开关等 40+ 项)的逐条含义与默认值见本专栏配置参数详解篇,本节只列与部署直接相关的骨架:
Revolt.toml(服务端主配置,generate_config.sh 生成后挂载进 8 个应用服务)
| 配置段 | 部署相关性 | 说明 |
|---|---|---|
[hosts] | 高 | 六个服务对外的完整 URL,客户端直连依据,域名/端口变化时必须整套同步 |
[hosts.livekit] | 高 | 语音服务的公网 WS 地址 |
[api.livekit.nodes.worldwide] | 中 | LiveKit 节点的内网地址(http://livekit:7880)与密钥 |
| 其余段 | 低 | 默认值即可跑通,按需再调 |
livekit.yml(语音服务配置,generate_config.sh 生成)
rtc:
use_external_ip: true # 使用服务器公网 IP 做 RTC
port_range_start: 50000 # UDP 端口段起
port_range_end: 50100 # UDP 端口段止
tcp_port: 7881 # RTC TCP 端口
redis:
address: redis:6379 # 复用编排内 Redis
turn:
enabled: false # TURN 中继默认关闭
keys:
<key>: <secret> # 与 secrets.env 的 LiveKit 密钥一致
webhook:
api_key: <key>
urls:
- "http://voice-ingress:8500/worldwide" # 信令回调
逐项解读:rtc 段决定音视频流的端口与地址发现方式;keys 段的密钥对必须与 secrets.env 中 REVOLT__API__LIVEKIT__NODES__WORLDWIDE__* 完全一致(generate_config.sh 生成时会自动对齐);webhook.urls 固定指向编排内的 voice-ingress,不要改。
八、配置文件挂载
理解挂载关系才能安全地改配置。compose.yml 中所有 bind mount(官方原值):
| 宿主文件 | 挂载点 | 挂载的服务 |
|---|---|---|
./Revolt.toml | /Revolt.toml | api、events、autumn、january、gifbox、crond、pushd、voice-ingress(8 个服务共用同一份) |
./Caddyfile | /etc/caddy/Caddyfile | caddy |
./stoat.json | /stoat.json | caddy(客户端发现配置,经 /.well-known/stoat 暴露) |
./livekit.yml | /etc/livekit.yml | livekit |
.env.web | (env_file 注入) | caddy、web |
secrets.env | (env_file 注入) | api、events、autumn、january、gifbox、crond、pushd、voice-ingress |
💡 说明:Caddyfile 的路由骨架值得通读一遍——它按路径把流量分发给各服务:
/api*→ api:14702、/ws→ events:14703、/autumn*→ autumn:14704、/january*→ january:14705、/gifbox*→ gifbox:14706、/livekit*→ livekit:7880、/ingress*→ voice-ingress:8500,其余全部落到 web:5000 前端。新增自建服务或调整路径时照这个骨架加 route 块即可。
由于 Revolt.toml 是 8 个服务共用的 bind mount,修改后需要重启全部应用服务:
docker compose restart api events autumn january gifbox crond pushd voice-ingress
九、等价 docker run
Stoat 是 16 服务协同的全家桶,官方明确只支持 compose 编排形态;逐服务手写 docker run 在技术上等价但需要手工复刻三件事:共享网络、健康检查依赖顺序、minio 的 7 个服务别名。以 api 服务为例给出等价命令(其余服务同理,仅镜像/挂载不同):
docker run -d --name api \
--network stoat_default \
--env-file secrets.env \
-v /opt/stoat/Revolt.toml:/Revolt.toml \
--restart always \
ghcr.io/stoatchat/api:v0.15.5
参数逐项说明:--network stoat_default 接入编排网络(服务间用服务名互访,如 mongodb://database);--env-file secrets.env 注入密钥(容器内以 REVOLT__XXX 环境变量存在,运行时覆盖 Revolt.toml 同名配置);-v 挂载主配置。minio 服务的等价命令还必须加 7 个网络别名(revolt-uploads.minio、attachments.minio、avatars.minio 等,服务端按「桶名.minio」的域名规则寻址),这就是不建议脱离编排手工拉起的主要原因——结论:生产环境一律使用 docker compose,本节的等价命令仅用于单服务调试对照。
十、常用运维命令
全部在编排目录(/opt/stoat/)内执行:
docker compose ps # 全家桶状态(healthy/Exited(0) 解读见 4.4)
docker compose logs -f api # 跟踪单个服务日志(服务名见 compose.yml)
docker compose logs --tail 100 events # 看事件服务最近 100 行
docker compose restart api # 重启单个服务
docker compose stop # 全家桶停止(保留容器与数据)
docker compose start # 全家桶启动
docker compose down # 全家桶停止并删容器(保留数据卷/bind mount)
docker compose down -v # 连同命名卷一起删除(bind mount 的 data/ 目录仍保留,需手动删)
docker compose pull # 拉取镜像更新
升级流程(官方编排形态下的标准做法):先看 self-hosted 仓库 compose.yml 的镜像标签是否更新 → git pull 拿新编排 → 备份 data/ 与 secrets.env → docker compose pull → docker compose up -d(Compose 自动重建镜像变化的容器)→ 按 4.5/4.6 验证。跨大版本升级前先在小环境演练,确认 MongoDB 数据兼容后再上生产。
十一、常见问题
Q1:database 容器反复重启,日志里出现启动崩溃/非法指令类错误?
官方编排默认 mongo:8。实测环境(内核 7.0.0)下该版本无法启动,改用 mongo:7 后正常。做法:编辑 compose.yml 把 image: docker.io/mongo:8 改为 image: docker.io/mongo:7,然后 docker compose up -d。实测 0.15.5 服务端在 MongoDB 7 上全功能可用(注册/消息/附件/实时事件均验证通过)。MongoDB 各版本对 CPU 指令集(如 AVX)与内核特性有硬性要求,无法升级 CPU/内核时可走降级路径。
Q2:宿主机 80/443 端口已被占用怎么办?
用 compose.override.yml 覆盖端口映射(见 4.8 实录),把 Caddy 映射到自选端口(如 8140),并把 Revolt.toml hosts、stoat.json、.env.web 三处 URL 整套同步为 http://IP:端口。只改端口不改 hosts 会出现「能打开页面但上传/预览失败」的错配。
Q3:注册时报 {"type":"CompromisedPassword"}?
0.15.5 内置了约 10 万条泄露密码黑名单(编译进服务端二进制),注册和登录两个环节都会校验,命中即拒绝(HTTP 400)。常见弱口令如 q1w2e3r4、password123 都在名单内。解决:换非泄露库口令(如 q1w2e3r4T!)。服务端还支持外接 easypwned 泄露密码查询服务(Revolt.toml [api.security] easypwned),配置后优先走外部查询。
Q4:对已存在的邮箱重复调用注册接口,返回 500 OperationFailed?
这不是真正的服务器故障:账号已存在时,注册接口会转入「补发验证邮件/密码重置」分支,而未配置 SMTP([api.smtp] host 为空)时该分支必然失败。遇到这个 500 应理解为「该邮箱已注册」,走登录流程即可。要消除此行为需正确配置 SMTP。
Q5:设置用户名时报 InvalidUsername?
用户名有内置保留名单,admin、revolt、stoat 三个名字(不区分大小写)不可使用,另有最小 2 字符、Unicode 规范化等校验。演示环境可用 rootadmin 之类的变体。
Q6:createbuckets 容器显示 Exited (0),是不是挂了?
不是。它是一次性初始化任务(在 MinIO 里创建 revolt-uploads 桶),完成后正常退出,退出码 0 即成功。其余 15 个服务应全部 Up。
Q7:/api/ 返回的 build 字段都是 <failed to generate>?
官方镜像构建时未注入 git 构建信息(commit_sha/semver 等),运行时拿不到就原样输出占位文本,不影响功能,可忽略。
Q8:升级后如何确认版本生效?
curl -s http://localhost/api/ | grep -o '"revolt":"[^"]*"',输出 "revolt":"0.15.5" 即当前服务端版本;前端版本看页面源码的 <title> 与资源 hash,或对比 for-web 镜像 tag。
十二、参考链接
- 官方自托管编排仓库:https://github.com/stoatchat/self-hosted(compose.yml/generate_config.sh/README/Guides,本文部署口径来源)
- 后端 monorepo:https://github.com/stoatchat/stoatchat(crates/core/config/Revolt.toml 默认配置)
- Stoat 官网:https://stoat.chat
- LiveKit 官网(语音子系统):https://livekit.io
- MongoDB 平台支持矩阵:https://www.mongodb.com/docs/manual/administration/production-notes/
- 认知与选型:《Stoat 0.15.5 介绍与选型指南》(含产品定位、16 服务架构拆解与 Mattermost/Rocket.Chat 量化对比)
- 配置参考:《Stoat 0.15.5 配置参数详解》(生成文件的逐项参数含义与生产模板)
- API 参考:《Stoat 0.15.5 API 使用详解》(注册/登录/建服/发消息全链路 curl 实测)
- 同类自托管 IM 的部署参照:《Rocket.Chat 8.9 介绍与选型指南》、《Mastodon 4.7.3 介绍与选型指南》(两者的编排形态与资源口径可与本文对照)