SSD Nodes Learn
指南 Matt Connor作者: Matt Connor · 已更新 2026-07-24

使用 Docker Compose 架設 Rocket.Chat 教學

本指南教您在 VPS 上使用 Docker Compose 部署 Rocket.Chat。解決 MongoDB 無法連接的 Replica Set 配置問題,並針對 2GB RAM 可能導致的 Out-of-memory 斷線問題提供硬體規格建議,包含 TLS 加密與備份方案。

專案目標

建立一個完全由您掌控的私有團隊聊天工具:在您的 VPS 上透過 Docker Compose 執行 Rocket.Chat,並使用 TLS 加密。所有訊息皆儲存在 MongoDB 資料庫中,方便您進行備份與遷移。Rocket.Chat 是成熟的開源軟體,可作為 Slack 或 Teams 的替代方案,提供頻道、私訊、回覆串、檔案分享以及語音與視訊功能,且全部運行在您租用並控制的硬體上。此應用程式為單一容器,可在幾分鐘內啟動。實際的錯誤通常發生在旁邊的資料庫,因此本指南的大部分內容將著重於 MongoDB,特別是初學者最容易忽略的一個要求:Rocket.Chat 無法連接至獨立的 MongoDB。它需要一個 replica set,即使該「set」僅包含單一節點。

前置作業,以及沒人告訴你的 RAM 計算方式

請誠實評估硬體規格。小型團隊的實際最低配置需求為 2 vCPU 與 4 GB RAM。Rocket.Chat 的 Node.js 程序本身約需 1 至 1.5 GB,而 MongoDB 的 WiredTiger 快取預設會佔用剩餘 RAM 的一半。在 2 GB 的 VPS 上,兩者在開機時尚能運行,但一旦出現實際流量就會發生衝突:MongoDB 擴張快取、Node 擴張 heap,導致 kernel 耗盡頁面(pages),隨後 out-of-memory killer 會殺掉佔用資源最大的程序,通常是 mongod。容器會輸出 Killed,Docker 嘗試重啟,導致聊天伺服器在負載尚在可承受範圍內時,每隔幾分鐘就斷線一次。2 GB 僅適合兩人測試使用,不適合作為團隊伺服器。建議從 4 GB 起步;若預期有數十名同時在線用戶、視訊通話或持續增加的上傳紀錄,請提供 8 GB。

在開始之前,您還需要準備三項條件。首先是擁有一個 A record 指向 VPS 公用 IP 的網域名稱——Rocket.Chat 的即時功能與行動裝置用戶端需要穩定的 hostname,而非單純的 IP。其次,伺服器防火牆與供應商的網路防火牆(多數控制面板中為獨立設定)皆須 開啟 80 與 443 埠。最後,需要一台擁有 root 或 sudo 權限的全新 Ubuntu 24.04 KVM VPS。如果您仍在考慮聊天伺服器是否適合作為第一個運行的服務,請參閱 2026 年值得自行架設的服務指南 以了解權衡。

安裝 Docker engine 與 Compose plugin

請使用 Docker 官方的 apt repository,不要使用 Ubuntu 內建的 docker.io 套件,也不要使用舊版的 docker-compose Python 二進位檔。現代的 Compose 是 Docker 的 plugin,呼叫指令時請使用 docker compose(中間為空格,而非連字號)。舊版的 docker-compose v1 已停止維護,且無法正確處理下方的 healthcheck 與 dependency 語法。

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 錯誤,代表 plugin 未安裝成功,這將導致後續出現難以排解的錯誤,請務必在此階段修正。

Compose 檔案:將 MongoDB 設定為單節點副本集 (single-node replica set)

這是最容易出錯的部分,請仔細閱讀。Rocket.Chat 使用 MongoDB change streams 功能,將新訊息即時推送至已連線的用戶端;而 change streams 僅在副本集 (replica set) 環境下可用。若將 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 的 port 是發佈在 127.0.0.1:3000 而非 0.0.0.0 —— 應用程式本身不具備 TLS 加密,因此僅應由同一台機器上的反向代理伺服器存取;若綁定到所有介面,會導致明文登入頁面直接暴露於公開網路。MongoDB 完全不對主機 (host) 進行 port 發佈;它僅能透過 Compose 的內部網路以 mongodb 名稱存取,而這正是 MONGO_URL 所使用的 hostname。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 旗標,用來告知該版本是否為值得固定使用的長期支援 (LTS) 版本,以避免需要頻繁手動維護伺服器。

初始化 replica set

啟動 stack:

sudo docker compose up -d

Rocket.Chat 會立即發生 crash,且 Docker 會不斷嘗試重啟它 — 這是正常現象,因為 replica set 尚未建立。請手動建立一次:

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 會使用容器的內部 hostname 來宣告 replica set — 例如 a1b2c3d4e5f6 這種隨機 hash 值。Rocket.Chat 從其自身的容器連線時,無法解析該名稱,導致 MongoDB driver 的 DNS 解析失敗,並不斷循環記錄 MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6。請務必使用與 MONGO_URL 相符的明確 service name 來啟動。

首次啟動:觀察啟動過程

當 Replica Set 設定為 Primary 後,Rocket.Chat 下次重啟將會正常連線並開始執行首次運行的資料庫遷移(migrations)。請追蹤日誌:

sudo docker compose logs -f rocketchat

您需要等待的內容是啟動橫幅(startup banner):

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

首次啟動速度較慢 —— 應用程式正在執行資料庫遷移並建立索引,請耐心等待一至兩分鐘。如果日誌重複出現 MongoServerSelectionError: Server selection timed out after 30000 ms 且拓撲類型(topology description)為 ReplicaSetNoPrimary,表示 Replica Set 未成功啟動;如果日誌在隨機雜湊值(random hash)上重複出現 getaddrinfo ENOTFOUND,表示啟動時使用了錯誤的主機名稱(host)。無論哪種情況,請退回上一步驟。一旦看到 SERVER RUNNING,表示 Rocket.Chat 已在 127.0.0.1:3000 進行監聽,此時應為其配置正式的主機名稱與 TLS。

使用 TLS 保護

切勿將 Rocket.Chat 直接暴露於 plain HTTP。若透過 http:// 登入,您的管理員密碼將會暴露給路徑上的任何使用者。請在同一台主機上的 reverse proxy 進行 TLS 終止,並將請求轉發至 127.0.0.1:3000。有兩點至關重要:首先,proxy 必須轉發 WebSocket upgrade headers,因為 Rocket.Chat 需要即時通訊功能,缺少這些 headers 將導致功能失效;其次,容器的 ROOT_URL 必須與使用者輸入的 public HTTPS 位址完全一致。

首先建立一個 plain HTTP nginx server block,將請求轉發至應用程式並轉發 upgrade headers。將其儲存為 /etc/nginx/sites-available/rocketchat,建立其至 sites-enabled 的 symlink,然後重新載入:

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;
    }
}

目前先維持在 port 80。若包含 listen 443 ssl; 但沒有憑證的設定,將無法通過 sudo nginx -t。重新載入 nginx (sudo nginx -t && sudo systemctl reload nginx),接著取得憑證。在 Ubuntu 上最簡便的方法是 使用 Certbot 與 nginx 取得 Let's Encrypt TLS 憑證certbot --nginx 會直接改寫上述的 block,加入 listen 443 ssl;ssl_certificate 內容,並自動建立 80-to-443 redirect,同時自動排程續期。若您已在單一 proxy 後執行多個容器,使用 Traefik 為多個 Docker 應用程式提供自動 TLS 是更整潔的選擇——只需在 rocketchat service 中加入 router 與 service labels,Traefik 就會自動處理請求並續期憑證,完全不需要 nginx block。無論採用哪種方式,請將 compose.yml 中的 ROOT_URL 設定為 https://chat.example.com,並重新執行 sudo docker compose up -d 以使容器套用變更。若您希望伺服器僅限內部網路存取而非公開網路,請使用 在 VPS 上架設 self-hosted WireGuard VPN 並將 proxy 綁定至 tunnel 位址。

首次執行設定精靈

瀏覽至 https://chat.example.com 並啟動 Rocket.Chat,系統會引導您完成簡短的精靈設定。首先是 admin account(管理員帳戶)——請輸入真實姓名、使用者名稱、電子郵件及強密碼;這是系統中唯一的帳戶,請務必妥善保管。接著是 organisation and server info(組織與伺服器資訊)——包含名稱、產業、規模、網站名稱與預設語言;此步驟僅為外觀設定,填寫後即可繼續。最後是關鍵抉擇:將此工作區 register this workspace 至 Rocket.Chat Cloud,或是保持 standalone(獨立運行)模式。

註冊後,可透過 Rocket.Chat 的閘道器與擴充套件市集使用行動裝置推播通知,但代價是伺服器會與 Rocket.Chat 的雲端建立控制平面(control-plane)關聯。若選擇 standalone,伺服器將保持完全私密且無外部依賴,但 iOS 與 Android 的推播通知將無法運作,因為 Apple 與 Google 不允許自行建置的 App 持有推播憑證——官方 App 必須透過雲端閘道器進行路由。若隱私是首要考量且使用者主要使用 Web App,請選擇 standalone;若行動裝置推播是必要需求,請選擇註冊。您稍後可以在 Admin 介面中更改此設定。

在邀請使用者前先進行安全性設定

Rocket.Chat 預設開啟 open registration on —— 註冊表單(Registration Form)預設為 Public,因此任何取得 URL 的人都能建立帳號。若使用公開的主機名稱,這等同於不設防。請前往 Admin → Settings → Accounts → Registration,將 Registration Form 設定為 Disabled(改由手動建立帳號或透過邀請連結),或是設定為 Secret URL。此外,除非您需要建立公開的唯讀頻道,否則請關閉 Allow Anonymous ReadAllow Anonymous Write

同時請決定檔案上傳的儲存位置。預設的 File Upload 儲存方式為 GridFS,這會將所有圖片與附件儲存在 MongoDB 內部。這種方式雖然簡單,但隨著使用者貼上截圖,您的資料庫(以及每一份 mongodump)會不斷膨脹。在 Admin → Settings → File Upload 中,您可以將儲存方式切換至 local filesystem 或 S3-compatible bucket,並設定合理的檔案大小上限。對於小團隊而言,使用 GridFS 是可行的,但請注意備份檔案會隨著時間變得越來越大。

使用 mongodump 進行備份

所有資料皆儲存在 mongodb_data 磁碟卷中。請勿直接複製執行中資料庫的磁碟卷;請使用 mongodump 進行一致性傾印 (dump),並將結果串流至主機檔案:

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

該 gzipped 壓縮檔包含完整的作業空間:使用者、頻道、訊息、設定,以及若您將上傳內容儲存在 GridFS 中的檔案。若您已將上傳內容移至檔案系統或 S3,請另外備份該儲存空間。若要在全新的環境進行還原,請先初始化 replica set,接著執行:

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

請將壓縮檔移出主機(例如儲存至 object storage、另一台伺服器,或任何不會隨 VPS 損毀而消失的地方),並設定 cron 每晚執行 dump。從未進行過還原的備份僅是「希望」而非真正的備份;請務必在測試用的 VPS 上練習還原,以確保在需要時功能正常。

Upgrades: pin tags, read the notes, respect the Mongo matrix

兩項規則可確保升級過程穩定。第一,每次僅升級一個 Rocket.Chat 大版本。系統會在啟動時執行 schema migrations,且不支援跨大版本跳躍;若嘗試從 6.x 直接升級至 8.x,系統會因 migration error 而停止,以避免損壞資料。請將 image tag 更新至下一個大版本的最新 release,閱讀該版本的 release notes 以確認 breaking changes,執行 docker compose up -d,並確認 logs 完成 migration 後再繼續。第二,遵循 MongoDB support matrix。每個 Rocket.Chat release 僅支援特定的 MongoDB 版本,請參考 curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions 進行確認。若要升級 MongoDB(例如從 7.0 升級至 8.0),請逐一升級大版本,並在每次跳轉後設定 feature-compatibility version。在 MongoDB 8.0 上,該指令需要明確的 confirm: true,否則系統會拒絕執行並提示需加上確認 flag 重新執行:

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

在升級任一組件前,請務必進行 mongodump。這是唯一的保險機制。

Failure modes, with the exact strings

Rocket.Chat 在 docker compose up 之後立即進入重啟迴圈,且 docker compose logs rocketchat 充滿了 MongoServerSelectionError MongoDB 雖在運行,但驅動程式無法選取 primary,錯誤字串會指出錯誤原因。Server selection timed out after 30000 ms 且 topology type 為 ReplicaSetNoPrimary 表示未執行過 rs.initiate() —— 此集合尚無設定。getaddrinfo ENOTFOUND 後接隨機 hash 表示啟動時未指定 host: "mongodb:27017",導致 MongoDB 廣播了無法解析的 container hostname。請使用 sudo docker compose exec mongodb mongosh --eval 'rs.status()' 進行診斷:若出現 MongoServerError: no replset config has been received 錯誤,請初始化集合;若顯示成員的 name 為隨機 hash,請使用 service name 重新初始化。

Web UI 可載入,但登入狀態會無限旋轉且無法完成。 開啟瀏覽器 console 會看到 WebSocket connection to 'wss://chat.example.com/websocket' failed。這通常是 ROOT_URL 不匹配,或是 proxy 未轉發 upgrade headers。請確認 ROOT_URL 等於包含 https:// 的完整公網位址,並確認 nginx location block 已使用 proxy_http_version 1.1 設定 UpgradeConnection "upgrade"。修改後請重新執行 docker compose up -d

Container 持續停止且 docker compose ps 顯示 Restarting docker compose logs 在行中斷掉,且 sudo dmesg | tail 顯示來自 oom-killer 的 Out of memory: Killed process 12345 (mongod);exit code 為 137。系統 RAM 不足。根本解決方案是升級 VPS —— 最低需 4 GB。臨時方案是在 command 中使用 --wiredTigerCacheSizeGB 1 限制 MongoDB 的 cache,但 swap 僅能延緩實際負載下的下一次 OOM:

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

docker compose upError response from daemon: driver failed programming external connectivity ... bind: address already in use 失敗。 有程序佔用了 port 3000 —— 通常是未正常停止的舊 Rocket.Chat container 或其他應用程式。請使用 sudo ss -ltnp | grep :3000 找出該程序或 container 並將其停止,或是將 mapping 的 host 端改為 127.0.0.1:3001:3000,並同步更新 proxy 的 proxy_pass

FAQ

Rocket.Chat 是否真的需要 MongoDB replica set?

是的,即使是僅包含單個資料庫節點的單機環境也是如此。Rocket.Chat 使用 MongoDB change streams 來傳送即時訊息,而 change streams 是 replica-set 專有的功能,單機版的 mongod 無法開啟此功能。您不需要多台機器;只需使用 --replSet rs0 啟動一個 MongoDB container,並透過 rs.initiate() 初始化一個單成員集(one-member set)。若跳過此步驟,驅動程式將無法找到 primary,導致 Rocket.Chat 因 MongoServerSelectionError: Server selection timed out 而陷入重啟迴圈,且無法完成啟動。

自架版 Rocket.Chat 需要多少 RAM?

建議以 4 GB 作為實際運作的最低配置,若團隊成員較多則建議配置 8 GB。Rocket.Chat 的 Node process 約佔用 1 至 1.5 GB,而 MongoDB 的 WiredTiger cache 會佔用剩餘 RAM 的約一半。因此在 2 GB 的主機上,兩者會發生衝突,且在任何實際負載下,out-of-memory killer 都會終止 mongod,並在日誌中顯示 Killed 與 exit code 137。2 GB 的配置僅足以讓您使用少量測試用戶來評估軟體。

如何為 Rocket.Chat 設定 HTTPS?

在同一台 VPS 上執行 reverse proxy 以終止 TLS 並轉發至 127.0.0.1:3000,並將 container 的 ROOT_URL 設定為您的公用 https:// 位址。Proxy 必須轉發 WebSocket upgrade headers,否則登入功能會卡住。使用 nginx 搭配 Certbot 是最簡單的單一應用程式設定;若您要在單一 proxy 後執行多個 container 並需要自動化憑證管理,則使用 Traefik 會更簡潔。

如何備份自架版 Rocket.Chat?

請使用 mongodump 進行一致性的資料庫 dump,而非直接複製 volume:docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz。該封存檔包含使用者、頻道、訊息與設定,若您將儲存空間設為 GridFS,則也會包含上傳的檔案。請將備份檔移出伺服器,並透過 cron 設定每晚自動執行,同時請在測試機上進行 mongorestore 演練,以確保還原功能正常。

如何在不損壞 MongoDB 的情況下升級 Rocket.Chat?

請一次僅升級一個 major version —— Rocket.Chat 會在啟動時執行 migrations,且不允許跳過 major 版本 —— 在更新 pinned image tag 之前,請務必閱讀每個版本的 release notes。請使用 curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions 確認目標版本支援的 MongoDB 版本;當您移動 MongoDB 時,請一次只升級一個 major 版本,並在每次跳轉後使用 confirm: true 設定 setFeatureCompatibilityVersion。操作前請務必先進行 mongodump