Stoat 0.15.5 Docker Compose 部署指南

用官方 self-hosted 编排从零把一套完整的 Stoat 0.15.5 自托管聊天平台跑起来,覆盖配置生成、全量拉起、入口验证、注册建服发消息的核心链路验证与数据持久化说明。已在 2C8G 的 Ubuntu 22.04 环境实测通过(入口 `http://192.168.1.200:8140`,注册/登录/建服/发消息/附件上传全链路验证可用)。

StoatStoatChat即时通讯2026/10/30

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 / ComposeDocker 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 实测):

服务官方镜像角色
apighcr.io/stoatchat/api:v0.15.5REST API(delta),全部业务入口
eventsghcr.io/stoatchat/events:v0.15.5WebSocket 实时事件(bonfire)
autumnghcr.io/stoatchat/file-server:v0.15.5文件上传下载服务
januaryghcr.io/stoatchat/proxy:v0.15.5链接预览与图片代理
gifboxghcr.io/stoatchat/gifbox:v0.15.5Tenor GIF 代理
crondghcr.io/stoatchat/crond:v0.15.5定时任务守护进程
pushdghcr.io/stoatchat/pushd:v0.15.5推送通知守护进程
voice-ingressghcr.io/stoatchat/voice-ingress:v0.15.5语音信令入口
livekitghcr.io/stoatchat/livekit-server:v1.9.14音视频 SFU
webghcr.io/stoatchat/for-web:4017c18Web 前端
caddydocker.io/caddy统一入口反代(按路径分发)
databasedocker.io/mongo:8(官方)/ mongo:7(实测降级)业务库
redisvalkey/valkey:9-alpine事件总线与 KV 缓存
rabbitdocker.io/rabbitmq:4内部队列
miniodocker.io/pgsty/siloS3 兼容对象存储
createbucketsdocker.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 的兼容说明。

三、前置条件

  1. 硬件:官方建议至少 2 vCPU、2 GB 内存(README Deployment 一节);实测 2C8G 下 16 个服务常驻内存约 2.5 GB,含语音视频使用建议 4 GB 起步。
  2. 软件:已安装 Docker Engine 与 Compose v2 插件,docker compose version 能正常输出版本号。
  3. 网络与域名(生产标准形态):
    • 域名 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。
  4. 依赖工具: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 逐行核读确认):

  1. 询问「是否置于其他反向代理之后」(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。
  2. 询问「是否启用摄像头与屏幕共享」(Would you like to enable camera and screen sharing? [Y/n])。答 Y(默认)→ 在 Revolt.toml 里写入 1080p 视频上限(video_resolution = [1920, 1080])。
  3. 生成三组密钥写入 secrets.env:VAPID 推送密钥对(openssl ecparam 生成 EC 私钥再转公钥)、文件加密密钥(openssl rand -base64 32)、LiveKit key/secret(openssl rand -hex)。
  4. 生成服务配置 Revolt.toml:hosts 段全部钉在 https://<你的域名>(api/events/autumn/january/gifbox 五个服务 URL + LiveKit 节点)。
  5. 生成 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/dbdatabaseMongoDB 业务库(用户/消息/服务器全部业务数据)
./data/miniominioS3 对象存储(头像/附件/表情等全部上传文件)
./data/rabbitrabbitRabbitMQ 队列数据
./data/caddy-datacaddyTLS 证书与站点数据
./data/caddy-configcaddyCaddy 动态配置
./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_DOMAINCaddy 网络别名的域名占位(caddy 服务的 network alias)标准部署填你的域名;反代模式留空

.env.web(Web 前端构建变量,由 generate_config.sh 生成)

变量作用取值
HOSTNAMEweb 服务监听地址标准部署 https://你的域名;反代/降级模式 :80
VITE_HOST前端自报主机名你的域名
VITE_API_URL前端调 API 的基准地址https://你的域名/api
VITE_DEV_GIFBOX_URLGIF 代理地址https://你的域名/gifbox

secrets.env(服务密钥,由 generate_config.sh 自动生成,格式 REVOLT__<段>__<键> 双下划线映射到 Revolt.toml 的配置路径)

变量映射到 Revolt.toml作用
REVOLT__PUSHD__VAPID__PRIVATE_KEY[pushd.vapid] private_keyWeb Push 推送的 VAPID 私钥
REVOLT__PUSHD__VAPID__PUBLIC_KEY[pushd.vapid] public_keyVAPID 公钥(浏览器订阅推送用)
REVOLT__FILES__ENCRYPTION_KEY[files] encryption_key上传文件的服务端加密密钥(丢失=文件不可读)
REVOLT__API__LIVEKIT__NODES__WORLDWIDE__KEY[api.livekit.nodes.worldwide] keyLiveKit 节点接入 key
REVOLT__API__LIVEKIT__NODES__WORLDWIDE__SECRET[api.livekit.nodes.worldwide] secretLiveKit 节点接入 secret

compose.yml 内固定写死的环境变量(官方默认值,可按需修改):

变量所属服务默认值说明
RABBITMQ_DEFAULT_USER / RABBITMQ_DEFAULT_PASSrabbitrabbituser / rabbitpass与 Revolt.toml [rabbit] 段保持一致,改要两边同步
MINIO_ROOT_USER / MINIO_ROOT_PASSWORDminiominioautumn / minioautumn与 Revolt.toml [files.s3] 的 access key 一致,改要两边同步
MINIO_DOMAINminiominioS3 虚拟主机域名

⚠️ 注意: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.tomlapi、events、autumn、january、gifbox、crond、pushd、voice-ingress(8 个服务共用同一份)
./Caddyfile/etc/caddy/Caddyfilecaddy
./stoat.json/stoat.jsoncaddy(客户端发现配置,经 /.well-known/stoat 暴露)
./livekit.yml/etc/livekit.ymllivekit
.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。

十二、参考链接