Rocket.ChatをDocker Composeで自宅運用する方法
VPSでRocket.ChatをDocker Compose運用する手順です。単一ノードでも必要なMongoDBレプリカセット、TLS、バックアップ、2 GBでOOMになる理由と対処法を解説します。
構築するもの
自分で完全に管理できるプライベートなチームチャットです。Docker Compose 上の自分の VPS で Rocket.Chat を実行し、TLS を終端し、すべてのメッセージをバックアップして移行できる MongoDB データベースに保存します。Rocket.Chat は、Slack や Teams に代わる成熟したオープンソースソフトウェアです。チャンネル、ダイレクトメッセージ、スレッド、ファイル共有、音声通話、ビデオ通話を、レンタルして管理できるハードウェア上で利用できます。アプリケーションは単一のコンテナとして数分で起動します。実際に発生する問題の多くは、隣接するデータベースにあります。そのため、このガイドの大半では 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 は2人で試すには十分ですが、チーム用サーバーには不十分です。まず 4 GB から始めてください。同時接続ユーザーが数十人になる場合、ビデオ通話を使用する場合、またはアップロード履歴が増える場合は、8 GB を割り当ててください。
開始前に、3つの準備も必要です。1つ目は、VPS のパブリック IP を指す A レコードを設定したドメイン名です。Rocket.Chat のリアルタイム機能とモバイルクライアントには、IP アドレスではなく、安定したホスト名が必要です。2つ目は、サーバーのファイアウォールと、プロバイダーのネットワークファイアウォールの両方で ポート 80 と 443 を開放することです。多くの管理パネルでは、後者は別の設定項目です。3つ目は、root または sudo を利用できる、初期状態の Ubuntu 24.04 KVM VPS です。チャットサーバーを最初に運用するサービスとして選ぶべきかまだ判断中であれば、2026 年にセルフホストする価値があるものについてのガイドで選択肢とトレードオフを確認できます。
Docker engine と Compose plugin をインストールする
Docker 独自の apt リポジトリを使用してください。Ubuntu に付属する docker.io パッケージや、古いスタンドアロンの docker-compose Python バイナリは使用しないでください。現在の Compose は Docker plugin です。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 versiondocker compose version を実行して Docker Compose version v2.x のような出力が表示されることが重要です。docker: 'compose' is not a docker command というエラーが表示される場合、plugin はインストールされていません。このまま進むと原因の分かりにくい失敗が発生するため、ここで修正してください。
Compose ファイル: MongoDB のシングルノードレプリカセット
ここは間違えやすい部分なので、ゆっくり読んでください。Rocket.Chat は接続中のクライアントへ新しいメッセージをリアルタイムで配信するために、MongoDB の change streams を使用します。change streams はレプリカセットでのみ利用できます。Rocket.Chat を通常のスタンドアロン mongod に接続すると、接続後に change stream の開始に失敗し、再起動を無限に繰り返します。特別な構成は必要ありません。通常の MongoDB コンテナを 1 つ起動し、--replSet を指定してから、1 メンバーのセットを初期化します。
作業ディレクトリと 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 と、8.5.1 のように Rocket.Chat のリリースを明示します。: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 -dRocket.Chat は直ちにクラッシュし、Docker は再起動を繰り返します。これは想定された動作です。まだレプリカセットが存在しないためです。次のコマンドを手動で 1 回実行して作成します。
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
+--------------------------------------------+初回起動には時間がかかります。アプリケーションはデータベースのマイグレーションを実行し、インデックスを作成するため、心配する前に 1〜2 分待ってください。ログに ReplicaSetNoPrimary 型のトポロジー情報とともに MongoServerSelectionError: Server selection timed out after 30000 ms が繰り返し表示される場合、レプリカセットが初期化されていません。ランダムなハッシュに対して getaddrinfo ENOTFOUND が繰り返し表示される場合は、誤ったホストで初期化されています。どちらの場合も、1 つ前の手順に戻ってください。SERVER RUNNING が表示されたら、Rocket.Chat は 127.0.0.1:3000 で接続を待ち受けています。実際のホスト名と TLS を前段に設定する段階です。
TLS の背後に配置する
Rocket.Chat を平文の HTTP で公開しないでください。http:// 経由で一度ログインすると、経路上の誰にでも管理者パスワードを渡したことになります。同じホスト上のリバースプロキシで TLS を終端し、127.0.0.1:3000 に転送します。重要な点は 2 つあります。Rocket.Chat はリアルタイム通信を使用するため、プロキシは WebSocket のアップグレードヘッダーを転送する必要があります。これがないと通信が機能しません。また、コンテナの ROOT_URL は、ユーザーが入力する公開 HTTPS アドレスと完全に一致させる必要があります。
まず、アプリにプロキシし、アップグレードヘッダーを転送する平文 HTTP の nginx server block を作成します。/etc/nginx/sites-available/rocketchat として保存し、sites-enabled にシンボリックリンクを作成してから、reload します。
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; を含み、証明書のない block は sudo nginx -t にも合格しません。nginx を reload(sudo nginx -t && sudo systemctl reload nginx)してから、証明書を発行します。Ubuntu で最も簡単なのは Certbot と nginx による Let's Encrypt TLS 証明書 です。certbot --nginx は上記の block をその場で書き換え、listen 443 ssl;、ssl_certificate の行、自動的な 80 から 443 へのリダイレクトを追加し、更新も自動でスケジュールします。すでに 1 つのプロキシの背後で複数のコンテナを運用している場合は、複数の Docker アプリで TLS を自動化する Traefik のほうが整理しやすい方法です。rocketchat service に router と service の label を追加すると、nginx の block は不要で、Traefik が証明書の要求と更新を自動で行います。どちらの場合も、compose.yml の ROOT_URL を https://chat.example.com に設定し、sudo docker compose up -d を再実行して、コンテナに変更を反映させます。サーバーをパブリックインターネットではなく自分のネットワーク内からのみ到達可能にする場合は、VPS 上で自己ホストする WireGuard VPN を前段に置き、プロキシをトンネルのアドレスに bind します。
初回セットアップウィザード
https://chat.example.comにアクセスすると、Rocket.Chat の短いウィザードが始まります。最初に、管理者アカウントの実名、ユーザー名、メールアドレス、強力なパスワードを設定します。存在するアカウントはこの 1 つだけなので、情報を失わないでください。次に、組織とサーバーの情報として、組織名、業種、規模、サイト名、デフォルト言語を設定します。これらは外観に関する項目なので、入力して先に進みます。続いて、実際に重要な選択があります。Rocket.Chat Cloud にこのワークスペースを登録するか、スタンドアロンで運用するかを選びます。
登録すると、Rocket.Chat のゲートウェイ経由のモバイルプッシュ通知とアドオンマーケットプレイスを利用できます。ただし、Rocket.Chat のクラウドとのコントロールプレーン接続が発生します。スタンドアロンでは、サーバーを完全に非公開で依存関係なく運用できます。一方、iOS と Android のプッシュ通知は動作しなくなります。Apple と Google が、自分でビルドしたアプリによるプッシュ証明書の保持を許可していないためです。公式アプリはクラウドゲートウェイを経由します。プライバシーを最優先し、ユーザーが Web アプリを主に利用する場合はスタンドアロンを選びます。モバイルプッシュが必須の場合は登録を選びます。選択は後で Admin から変更できます。
招待する前にアクセスを制限する
Rocket.Chat はデフォルトで登録が有効になっており、Registration Form は Public に設定されています。そのため、URL を見つけた人は誰でもアカウントを作成できます。公開ホスト名でこの状態にすると、誰でも入れる状態になります。Admin → Settings → Accounts → Registration に移動し、Registration Form を Disabled に設定してください。これにより、アカウントを手動または招待リンクで作成できます。または Secret URL に設定します。この画面で、特に公開読み取り専用チャンネルが必要でない限り、Allow Anonymous Read と Allow Anonymous Write も無効にしてください。すべてのアカウントを手動で作成するのが面倒で、これがチームのログイン先の唯一のサービスでもない場合は、Rocket.Chat の OAuth ログインを セルフホストの Authentik SSO サーバー に接続してください。これにより、参加者と離脱者の管理をアプリごとではなく、1 か所でまとめて処理できます。
アップロード先も決めておきます。デフォルトの File Upload ストレージは GridFS です。すべての画像と添付ファイルが MongoDB 内に保存されます。これは簡単ですが、スクリーンショットを貼り付けるたびにデータベースと、取得するすべての mongodump が際限なく大きくなります。Admin → Settings → File Upload では、ストレージをローカルファイルシステムまたは S3 互換バケットに変更し、妥当な最大ファイルサイズを設定できます。小規模なチームであれば 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 の対応表に従う
アップグレードを安全に進めるためのルールは2つあります。1つ目は、Rocket.Chat はメジャーバージョンを一度に1つだけ上げることです。 起動時にスキーマ移行を実行し、メジャーバージョンをまたいだアップグレードを意図的に拒否します。6.x から 8.x へ直接移行しようとすると、データを破損させる代わりに移行エラーで停止します。イメージタグを次のメジャーバージョンの最新リリースに変更し、そのリリースのリリースノートで破壊的変更を確認してから docker compose up -d を実行します。次のメジャーバージョンへ進む前に、ログを監視し、移行が完了したことを確認します。2つ目は、MongoDB の対応表に従うことです。 Rocket.Chat の各リリースは、特定の MongoDB バージョンに対応しており、curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions で確認できます。MongoDB を 7.0 から 8.0 のように更新する場合も、メジャーバージョンを一度に1つだけ上げ、各段階の後に feature compatibility version を設定します。MongoDB 8.0 では、このコマンドに明示的な confirm: true が必要です。指定しない場合は、確認フラグを付けて再実行するよう求めるメッセージが表示され、コマンドは拒否されます。
sudo docker compose exec mongodb mongosh --eval 'db.adminCommand({setFeatureCompatibilityVersion: "8.0", confirm: true})'いずれかのコンポーネントをアップグレードする前に、毎回 mongodump を取得します。これが必要な保険のすべてです。
障害パターンと正確なエラー文字列
docker compose up の直後に Rocket.Chat が再起動ループに入り、docker compose logs rocketchat に MongoServerSelectionError が出力され続ける。 MongoDB は起動していますが、ドライバーがプライマリを選択できていません。正確な文字列から、行った設定ミスを特定できます。トポロジータイプが ReplicaSetNoPrimary の Server selection timed out after 30000 ms は、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 ブロックで proxy_http_version 1.1 を使用して Upgrade と Connection "upgrade" が設定されていることも確認します。どちらかを変更した場合は、docker compose up -d を再実行します。
コンテナが停止を繰り返し、docker compose ps に Restarting と表示される。 docker compose logs は行の途中で途切れ、sudo dmesg | tail には oom-killer による Out of memory: Killed process 12345 (mongod) が表示されます。終了コードは 137 です。サーバーの RAM が不足しています。根本的な解決策は、4 GB 以上の大きな VPS に移行することです。一時的な対策として swap を追加し、command 内の --wiredTigerCacheSizeGB 1 で MongoDB のキャッシュを制限できます。ただし、実際の負荷がかかると swap は次の OOM を遅らせるだけです。
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfiledocker 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 のレプリカセットが必要ですか?
はい。データベースノードが 1 つだけの単一サーバーでも必要です。Rocket.Chat は MongoDB の change streams を使用してメッセージをリアルタイムで配信します。change streams はレプリカセット専用の機能であり、スタンドアロンの mongod では開けません。複数のマシンは必要ありません。--replSet rs0 で起動した MongoDB コンテナを 1 つ実行し、rs.initiate() で 1 メンバーのセットを初期化します。この手順を省くと、ドライバーは primary を見つけられません。そのため、Rocket.Chat は MongoServerSelectionError: Server selection timed out で再起動を繰り返し、起動を完了できません。
self-hosted Rocket.Chat にはどの程度の RAM が必要ですか?
実用上の最小値として 4 GB、利用者の多いチームでは 8 GB を見込んでください。Rocket.Chat の Node プロセスは約 1〜1.5 GB を使用します。MongoDB は残りの RAM のおよそ半分を WiredTiger キャッシュに確保します。そのため、2 GB のサーバーでは両者が RAM を奪い合い、実際の負荷がかかると out-of-memory killer が mongod を終了させます。ログには Killed が表示され、終了コードは 137 になります。2 GB で十分なのは、テストユーザー数名でソフトウェアを評価する場合だけです。
Rocket.Chat を HTTPS の背後に置くにはどうすればよいですか?
同じ VPS で TLS 終端を担うリバースプロキシを実行し、127.0.0.1:3000 に転送します。コンテナの ROOT_URL には、公開用の https:// アドレスを設定します。プロキシは WebSocket の upgrade ヘッダーを転送する必要があります。転送しないと、ログインがハングします。単一アプリの構成では、nginx と Certbot の組み合わせが最も簡単です。1 つのプロキシの背後で複数のコンテナを実行し、証明書管理を自動化する場合は、Traefik の方が適しています。
self-hosted Rocket.Chat をバックアップするにはどうすればよいですか?
ボリュームをコピーするのではなく、mongodump で整合性のあるデータベースダンプを取得します。docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz。このアーカイブには、ユーザー、チャンネル、メッセージ、設定が含まれます。ストレージを GridFS にしている場合は、アップロードしたファイルも含まれます。アーカイブをサーバー外へコピーし、cron で毎晩自動実行します。さらに、使い捨てのサーバーで mongorestore を実施し、実際にリストアできることを確認してください。
MongoDB を壊さずに Rocket.Chat をアップグレードするにはどうすればよいですか?
Rocket.Chat はメジャーバージョンを 1 つずつ上げてください。起動時にマイグレーションを実行し、メジャーバージョンのスキップを拒否します。固定しているイメージタグを変更する前に、各リリースのリリースノートを確認してください。対象のリリースがサポートする MongoDB のバージョンは curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions で確認します。MongoDB を更新する場合も、メジャーバージョンを 1 つずつ上げ、各段階の後に confirm: true で setFeatureCompatibilityVersion を設定します。必ず最初に mongodump を取得してください。