SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-26

Rocket.Chat Docker Compose 自托管部署指南

在 VPS 上用 Docker Compose 部署 Rocket.Chat,配置单节点 MongoDB 副本集、TLS 和备份,并修复“not running with --replSet”及 OOM 重启等常见故障。

您将构建的内容

一个完全由您掌控的私有团队聊天系统:Rocket.Chat 运行在您自己的 VPS 上,由 Docker Compose 管理,通过 TLS 终止连接,所有消息都存储在 MongoDB 数据库中,您可以对其进行备份和迁移。Rocket.Chat 是成熟的开源 Slack 和 Teams 替代方案,提供频道、直接消息、线程、文件共享以及语音和视频功能,全部运行在您租用并控制的硬件上。不过,它并不是唯一可靠的选项。如果您仍在选择,Mattermost、Rocket.Chat、Synapse 和 Zulip 对比会从后续容易出问题的方面进行比较:RAM、数据库、移动推送、SSO 和升级。应用本身是一个容器,几分钟内即可启动。实际导致故障的内容都在旁边的数据库中,因此本指南大部分内容都围绕 MongoDB,尤其是第一个会让所有人首次遇到时感到意外的要求:Rocket.Chat 无法连接到独立运行的 MongoDB。它需要副本集,即使这个“集合”只有一个节点。

前置条件,以及没人告诉你的 RAM 计算

请如实评估服务器配置。小团队的实际最低配置是 2 vCPU 和 4 GB RAM。Rocket.Chat 的 Node.js 进程本身大约需要 1 到 1.5 GB,MongoDB 的 WiredTiger 缓存默认会占用剩余 RAM 的约一半。在 2 GB VPS 上,两者启动时可以勉强运行,但真实流量一到就会发生冲突:MongoDB 扩大缓存,Node 扩大堆,内核耗尽页面,out-of-memory killer 会终止占用最大的进程,通常是 mongod。容器会输出 Killed,Docker 随后重启容器。结果是,聊天服务器在本应轻松应对的负载下每隔几分钟就会中断。2 GB 适合两个人试用,但不适合作为团队服务器。请从 4 GB 起步。如果预计有几十个并发用户、视频通话或不断增长的上传记录,请使用 8 GB。还要为服务器承载的其他服务预留资源:如果在同一 VPS 上部署 Notion 风格的 AFFiNE 工作区,还会增加 4 个容器来争用相同的内存页面。因此,这些容器需要额外的 RAM,不能占用 Rocket.Chat 已需的 RAM。

开始前还需要准备 3 项内容。首先是一个域名,并配置指向 VPS 公网 IP 的 A 记录。Rocket.Chat 的实时功能和移动客户端需要稳定的主机名,不能只使用裸 IP。其次,在服务器防火墙和服务商的网络防火墙上开放 80 和 443 端口。在大多数控制面板中,后者是独立的控制项。最后,需要一台全新的 Ubuntu 24.04 KVM VPS,并拥有 root 或 sudo 权限。如果你还在判断聊天服务器是否适合作为第一个要运行的服务,请参阅 2026 年哪些服务值得自行托管的指南,其中介绍了相关取舍。

安装 Docker 引擎和 Compose 插件

使用 Docker 自己的 apt 软件源,不要使用 Ubuntu 随附的 docker.io 软件包,也不要使用古老的独立 docker-compose Python 二进制文件。现代 Compose 是 Docker 插件,应使用 docker compose 调用,中间是空格而不是连字符。旧版 docker-compose v1 已停止维护,无法正确处理下面的 healthcheck 和依赖语法。

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

确认这两个组件都已存在:

sudo docker version
sudo docker compose version

docker compose version 输出类似 Docker Compose version v2.x 的内容,才表示检查通过。如果出现 docker: 'compose' is not a docker command 错误,说明插件未安装。后续操作会出现难以判断的故障,因此应立即在此处修复。

Compose 文件:MongoDB 作为单节点副本集

这一部分最容易出错,请仔细阅读。Rocket.Chat 使用 MongoDB change streams,向已连接的客户端实时推送新消息;而 change streams 只有在副本集中可用。将 Rocket.Chat 指向普通独立 mongod 后,它会成功连接,但无法打开 change stream,随后会无限进入重启循环。解决方法并不复杂:运行一个普通的 MongoDB 容器,启动时添加 --replSet,然后初始化一个单成员副本集。

创建工作目录和一个 compose.yml

services:
  mongodb:
    image: mongo:8.0
    restart: always
    command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--oplogSize", "128"]
    volumes:
      - mongodb_data:/data/db
      - mongodb_config:/data/configdb
    healthcheck:
      test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 10s
      retries: 12

  rocketchat:
    image: registry.rocket.chat/rocketchat/rocket.chat:8.5.1
    restart: always
    depends_on:
      mongodb:
        condition: service_healthy
    environment:
      MONGO_URL: "mongodb://mongodb:27017/rocketchat?replicaSet=rs0"
      MONGO_OPLOG_URL: "mongodb://mongodb:27017/local?replicaSet=rs0"
      ROOT_URL: "https://chat.example.com"
      PORT: "3000"
    ports:
      - "127.0.0.1:3000:3000"

volumes:
  mongodb_data:
  mongodb_config:

这里的几项配置是有意这样设置的。Rocket.Chat 端口发布到 127.0.0.1:3000,而不是 0.0.0.0。应用本身没有 TLS,因此只有同一台服务器上的反向代理应能访问它;将其绑定到所有网络接口,会直接把明文登录页面暴露到公网。MongoDB 完全不发布到主机,只能通过 Compose 的内部网络访问,主机名为 mongodb;这正是 MONGO_URL 使用的主机名。MONGO_URL 携带 ?replicaSet=rs0。如果省略该配置,驱动会将服务器视为独立实例,即使它实际是副本集,change streams 仍会失败。MONGO_OPLOG_URL 指向保存 oplog 的 local 数据库;现代 Rocket.Chat 更倾向于使用 change streams,但设置该参数没有副作用,也能兼容旧代码路径。depends_on 使用 condition: service_healthy,因此 Compose 会先等待 MongoDB 对 ping 作出响应,再启动 Rocket.Chat;这就是 healthcheck 的作用。

为两个镜像固定真实的版本标签:mongo:8.0,以及此处使用的明确 Rocket.Chat 版本,例如 8.5.1。绝不要使用 :latest,否则无人值守的 docker pull 会意外升级,且无法迁移。固定版本前,先检查当前稳定版 Rocket.Chat 版本及其支持的 MongoDB 版本。

Rocket.Chat 会为每个版本发布机器可读的信息文档:curl -s https://releases.rocket.chat/8.5.1/info | jq '{compatibleMongoVersions, lts}' 对 8.5.1 返回 compatibleMongoVersions: ["8.0"],因此 mongo:8.0 是唯一受支持的引擎;文档还包含 lts 标志,用于说明该版本是否为长期支持版本,适合不希望频繁维护服务器时固定使用。并非每个项目都会发布带版本的镜像。遇到这种情况,应在源代码层面固定版本:自行托管 openGym 锻炼跟踪器意味着检出特定的 git 标签并从该标签构建,而不是跟随持续变化的分支。

初始化副本集

启动整个堆栈:

sudo docker compose up -d

Rocket.Chat 会立即崩溃,Docker 也会不断重启它。这是预期行为,因为副本集尚不存在。手动创建一次:

sudo docker compose exec mongodb mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "mongodb:27017"}]})'

正确结果应为 { ok: 1 }。几秒内,单节点会自行选举为 primary。使用以下命令确认:

sudo docker compose exec mongodb mongosh --quiet --eval 'rs.status().members[0].stateStr'

您应看到 PRIMARY。本页最重要的细节是 host: "mongodb:27017" 参数。如果在没有成员列表的情况下直接运行 rs.initiate(),MongoDB 会使用容器的内部主机名公布副本集,例如随机生成的哈希名 a1b2c3d4e5f6。Rocket.Chat 从自身容器连接时无法解析该名称,因此 MongoDB 驱动程序会在此处发生 DNS 解析失败,并不断记录 MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6。始终使用与您的 MONGO_URL 匹配的显式服务名进行初始化。

首次启动:监控启动过程

设置为 primary 后,Rocket.Chat 下次重启时会正常连接,并开始首次启动迁移。查看日志:

sudo docker compose logs -f rocketchat

需要等待的内容是启动横幅:

+--------------------------------------------+
        SERVER RUNNING
   Rocket.Chat Version: 8.5.1
        NodeJS Version: 22.22.3 - x64
+--------------------------------------------+

首次启动较慢,因为应用会运行数据库迁移并创建索引。因此,请先等待一到两分钟。如果日志反复输出 MongoServerSelectionError: Server selection timed out after 30000 ms,并显示类型为 ReplicaSetNoPrimary 的拓扑描述,则说明副本集尚未初始化;如果日志对随机哈希反复输出 getaddrinfo ENOTFOUND,则说明初始化时使用了错误的主机。无论哪种情况,都返回上一步。看到 SERVER RUNNING 后,Rocket.Chat 已在 127.0.0.1:3000 上监听,此时可以为其配置正式主机名和 TLS。

置于 TLS 后

切勿让 Rocket.Chat 直接通过 HTTP 暴露。通过 http:// 登录一次,就等于将管理员密码交给链路上的任何人。在同一台服务器上的反向代理中终止 TLS,再将请求转发到 127.0.0.1:3000。有两点需要注意:代理必须转发 WebSocket 升级请求头,因为 Rocket.Chat 依赖实时通信,缺少这些请求头就会失效;容器中的 ROOT_URL 必须与用户输入的公网 HTTPS 地址完全一致。

先创建一个普通的 HTTP nginx server block,将请求代理到应用,并转发升级请求头。将其保存为 /etc/nginx/sites-available/rocketchat,再创建符号链接到 sites-enabled,然后重新加载 nginx:

server {
    listen 80;
    server_name chat.example.com;

    client_max_body_size 100M;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

暂时保留 80 端口。包含 listen 443 ssl; 但没有证书的配置甚至无法通过 sudo nginx -t。重新加载 nginx(sudo nginx -t && sudo systemctl reload nginx),然后申请证书。在 Ubuntu 上,最简便的方法是使用 使用 Certbot 和 nginx 配置 Let's Encrypt TLS 证书certbot --nginx 会直接改写上面的配置,添加 listen 443 ssl;ssl_certificate 行以及自动从 80 重定向到 443 的规则,并为你安排证书续期。如果你已经通过一个代理运行多个容器,为多个 Docker 应用配置自动 TLS 的 Traefik 更简洁。只需将路由器和服务标签添加到 rocketchat 服务,Traefik 就会代你申请和续期证书,无需 nginx 配置块。无论采用哪种方式,都应在 compose.yml 中将 ROOT_URL 设置为 https://chat.example.com,然后重新运行 sudo docker compose up -d,使容器加载更改。如果只希望服务器可从自有网络内部访问,而不是暴露到公网,请在前面配置 VPS 上的自托管 WireGuard VPN,并将代理绑定到隧道地址。

首次运行设置向导

访问 https://chat.example.com,Rocket.Chat 会引导您完成一个简短的设置向导。首先设置管理员帐户,包括真实姓名、用户名、电子邮件地址和强密码;这是系统中唯一存在的帐户,请务必妥善保存凭据。接着填写组织和服务器信息,包括名称、所属行业、规模、站点名称和默认语言;这些设置只影响显示效果,填写后继续即可。然后选择真正重要的选项:将此工作区注册到 Rocket.Chat Cloud,或将其保持为独立模式

注册后,系统可通过 Rocket.Chat 的网关提供移动推送通知,并启用附加组件市场,但代价是服务器需要与 Rocket.Chat 云端建立控制平面关系。独立模式可让服务器完全保持私有且不依赖外部服务,但 iOS 和 Android 推送通知将停止工作,因为 Apple 和 Google 不允许自建应用自行持有推送证书,官方应用必须通过云端网关转发推送。若隐私是首要目标,且用户主要使用 Web 应用,请选择独立模式;若移动推送不可或缺,请选择注册。之后可在 Admin 中更改此设置。

在邀请任何人之前先收紧配置

Rocket.Chat 默认开启开放注册,Registration Form 默认设置为 Public,因此任何找到 URL 的人都可以创建帐户。在公网主机名上,这相当于门户大开。转到管理 → 设置 → 帐户 → 注册,将 Registration Form 设置为 Disabled,这样就可以手动创建帐户或通过邀请链接创建帐户;也可以设置为 Secret URL。在此页面中,同时关闭 Allow Anonymous ReadAllow Anonymous Write,除非您确实需要公开的只读频道。如果手动创建每个帐户过于繁琐,并且这不是团队成员登录的唯一服务,请将 Rocket.Chat 的 OAuth 登录指向自托管的 Authentik SSO 服务器。这样可以在一个位置统一处理成员加入和离开,而不必在每个应用中分别操作。

同时确定上传文件的存储位置。默认的 File Upload 存储方式是 GridFS,它会将每张图片和每个附件直接存储在 MongoDB 中。这样配置简单,但意味着随着用户不断粘贴屏幕截图,数据库以及您创建的每个 mongodump 都会持续增长。转到管理 → 设置 → 文件上传,可以将存储方式切换为本地文件系统或兼容 S3 的存储桶,并设置合理的最大文件大小。如果团队交换的是完整的照片库,而不是偶尔发送屏幕截图,应将这些文件存储在专用照片服务器中,而不是聊天数据库中;PhotoPrism 与 Immich 对比会比较两者的内存开销和所需的备份命令。对于小型团队,GridFS 足够使用,但请注意备份会随时间变得越来越大。

使用 mongodump 备份

所有数据都位于 mongodb_data 卷中。不要在数据库运行时直接复制该卷。应使用 mongodump 生成一致性转储,并将其流式写入主机上的文件:

sudo docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > rocketchat-$(date +%F).archive.gz

这个 gzip 压缩归档包含整个工作区:用户、频道、消息、设置,以及在将上传文件保留在 GridFS 中时的文件。如果已将上传文件迁移到文件系统或 S3,还应单独备份该存储。将数据恢复到全新的服务栈时,先初始化副本集,然后运行:

sudo docker compose exec -T mongodb mongorestore --archive --gzip --drop < rocketchat-2026-07-15.archive.gz

将归档复制到主机之外,例如对象存储或另一台服务器。无论 VPS 发生什么故障,备份都不应与其一同丢失。然后通过 cron 每晚运行转储。未经恢复验证的备份只是希望,不能算备份。请先在一次性 VPS 上练习恢复,确认流程可用后再依赖它应对故障。

升级:固定标签、阅读说明、遵循 MongoDB 兼容矩阵

遵循两条规则,升级过程就能保持简单。第一,每次只将 Rocket.Chat 升级一个主版本。 Rocket.Chat 启动时会执行架构迁移,并且会主动拒绝跨主版本升级;如果尝试从 6.x 直接升级到 8.x,它会因迁移错误停止,而不是损坏数据。将镜像标签更新为下一个主版本的最新发行版,阅读该发行版的说明以了解不兼容变更,运行 docker compose up -d,并监控日志,确认迁移完成后再继续。第二,遵循 MongoDB 支持矩阵。 每个 Rocket.Chat 发行版支持特定的 MongoDB 版本范围,curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions 会告诉您具体支持哪些版本。升级 MongoDB 时,例如从 7.0 升级到 8.0,也应每次只跨一个主版本,并在每次升级后设置 feature compatibility version。在 MongoDB 8.0 中,该命令要求显式指定 confirm: true;否则命令会拒绝执行,并提示您使用确认标志重新运行:

sudo docker compose exec mongodb mongosh --eval 'db.adminCommand({setFeatureCompatibilityVersion: "8.0", confirm: true})'

在升级任一组件前,都应执行 mongodump。这就是全部的保险措施。

故障模式及精确字符串

Rocket.Chat 在 docker compose up 后立即反复重启,docker compose logs rocketchat 中不断出现 MongoServerSelectionError MongoDB 正在运行,但驱动无法选择主节点。精确字符串可以指出配置错误。Server selection timed out after 30000 ms 的拓扑类型为 ReplicaSetNoPrimary,表示你从未运行 rs.initiate(),副本集尚未配置。getaddrinfo ENOTFOUND 后跟随机哈希,表示你未使用显式的 host: "mongodb:27017" 启动副本集,因此 MongoDB 广播了无法解析的容器主机名。使用 sudo docker compose exec mongodb mongosh --eval 'rs.status()' 进行诊断:如果返回 MongoServerError: no replset config has been received,请初始化副本集;如果显示某个成员的 name 为随机哈希,请使用服务名重新初始化。

Web UI 可以加载,但登录一直转圈且无法完成。 打开浏览器控制台,你会看到 WebSocket connection to 'wss://chat.example.com/websocket' failed。这几乎总是 ROOT_URL 不匹配,或代理未转发升级标头导致的。确认 ROOT_URL 与包含 https:// 的完整公网地址完全一致,并确认 nginx 的 location 块设置了 UpgradeConnection "upgrade",值为 proxy_http_version 1.1。修改其中任一项后,重新运行 docker compose up -d

容器不断退出,docker compose ps 显示其 Restarting docker compose logs 在中途截断,sudo dmesg | tail 显示 oom-killer 产生的 Out of memory: Killed process 12345 (mongod);退出代码为 137。服务器内存不足。真正的解决方案是使用更大的 VPS,最低需要 4 GB。临时措施是添加 swap,并在 MongoDB 的 command 中使用 --wiredTigerCacheSizeGB 1 限制缓存,但在实际负载下,swap 只能延缓下一次 OOM:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

docker compose up 失败并显示 Error response from daemon: driver failed programming external connectivity ... bind: address already in use 已有其他进程占用端口 3000,通常是未正常停止的旧 Rocket.Chat 容器,也可能是其他应用。使用 sudo ss -ltnp | grep :3000 找到占用者,然后停止该进程或容器;也可以将映射的主机端改为 127.0.0.1:3001:3000,并相应更新代理中的 proxy_pass

FAQ

Rocket.Chat 确实需要 MongoDB 副本集吗?

需要,即使只有一台服务器和一个数据库节点也是如此。Rocket.Chat 使用 MongoDB change streams 实时传递消息,而 change streams 仅支持副本集,独立运行的 mongod 无法创建 change stream。您不需要多台机器,只需运行一个使用 --replSet rs0 启动的 MongoDB 容器,再使用 rs.initiate() 初始化单成员副本集。跳过这一步后,驱动程序始终找不到 primary,Rocket.Chat 会因 MongoServerSelectionError: Server selection timed out 反复重启,始终无法完成启动。

自托管 Rocket.Chat 需要多少 RAM?

实际最低建议准备 4 GB,繁忙团队建议使用 8 GB。Rocket.Chat 的 Node 进程约使用 1 到 1.5 GB,MongoDB 会为 WiredTiger 缓存占用剩余 RAM 的大约一半。因此,在 2 GB 的服务器上,两者会争用内存;只要有实际负载,out-of-memory killer 就会终止 mongod,日志中显示 Killed,退出码为 137。2 GB 仅适合使用几个测试用户评估软件。

如何让 Rocket.Chat 使用 HTTPS?

在同一台 VPS 上运行反向代理,由代理终止 TLS 并将请求转发到 127.0.0.1:3000,同时将容器的 ROOT_URL 设置为您的公网 https:// 地址。代理必须转发 WebSocket upgrade headers,否则登录会一直卡住。对于单应用部署,Certbot 配合 nginx 是最简单的方案;如果您要在一个代理后运行多个容器,并希望自动管理证书,Traefik 更合适。

如何备份自托管的 Rocket.Chat?

使用 mongodump 创建一致的数据库转储,不要直接复制卷:docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz。如果您仍将文件存储在 GridFS 中,该归档包含用户、频道、消息和设置,以及上传的文件。将归档复制到服务器外部,使用 cron 每晚自动执行备份,并在临时服务器上演练 mongorestore,以确认恢复确实可用。

如何升级 Rocket.Chat,同时避免破坏 MongoDB?

每次只升级 Rocket.Chat 一个 major version。Rocket.Chat 会在启动时运行迁移,并拒绝跳过 major version。升级固定的 image tag 前,请先阅读对应版本的发布说明。使用 curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions 检查目标版本支持的 MongoDB 版本。升级 MongoDB 时,每次只跨越一个 major version,并在每次升级后使用 confirm: true 设置 setFeatureCompatibilityVersion。始终先创建 mongodump