Docker ComposeでRocket.Chatをセルフホストする方法
Docker ComposeとVPSを用いてRocket.Chatを構築する手順を解説します。MongoDBをreplica setとして構成する必須設定や、2GB RAM環境で発生するOOM killerによるコンテナの再起動問題の回避策、TLS設定、バックアップ方法まで詳しく説明します。
作成するもの
自身で完全に管理するプライベートなチームチャットです。Docker Composeを使用して、自身のVPS上でRocket.Chatを稼働させます。TLSで暗号化し、すべてのメッセージはバックアップや移行が可能なMongoDBデータベースに保存されます。Rocket.Chatは、SlackやTeamsの代わりとなる成熟したオープンソースソフトウェアです。チャンネル、ダイレクトメッセージ、スレッド、ファイル共有、音声・ビデオ通話のすべてを、自身でレンタル・管理するハードウェア上で利用できます。アプリケーションは単一のコンテナであり、数分で起動します。トラブルの主な原因は隣接するデータベースにあります。そのため、本ガイドの大部分はMongoDBに関する内容です。特に、初めて利用する人が驚く重要な要件があります。Rocket.Chatは、スタンドアロンのMongoDBでは動作しません。たとえ1ノードのみの構成であっても、レプリカセット(replica set)として構成する必要があります。
前提条件、および誰も教えてくれないRAMの計算
サーバーのスペックを正確に見積もってください。小規模チームにおける現実的な最小構成は 2 vCPU と 4 GB の RAM です。Rocket.Chat の Node.js プロセスだけで約 1 〜 1.5 GB を消費します。また、MongoDB の WiredTiger キャッシュは、デフォルトで残りの RAM の約半分を占有します。2 GB の VPS では、起動時は両方のプロセスが収まりますが、実際のトラフィックが発生した瞬間に衝突します。MongoDB がキャッシュを増やし、Node がヒープを増やし、カーネルの空きページが枯渇します。すると、OOM killer が最もメモリを消費しているプロセスを強制終了させます。通常は mongod です。コンテナは Killed を出力し、Docker は再起動を繰り返します。その結果、本来耐えられるはずの負荷で、数分おきにチャットサーバーがダウンします。2 GB は 2 人程度のテスト用としては十分ですが、チーム用サーバーとしては不十分です。まずは 4 GB から始めてください。数十人の同時接続ユーザー、ビデオ通話、または増え続けるアップロード履歴を想定する場合は 8 GB を割り当ててください。
開始前に、以下の 3 つを準備する必要があります。まず、VPS のパブリック IP を指す A record を設定したドメイン名が必要です。Rocket.Chat のリアルタイム機能やモバイルクライアントには、IP アドレスではなく安定したホスト名が必要だからです。次に、サーバーのファイアウォールと、プロバイダーのネットワーク・ファイアウォールの両方で Port 80 と 443 を開放してください。多くの管理パネルでは、これらは別々の設定項目になっています。最後に、root または sudo 権限を持つ、新規の Ubuntu 24.04 KVM VPS を用意してください。チャットサーバーを最初に運用すべきサービスかどうか迷っている場合は、2026年にセルフホストすべきサービスガイド でトレードオフを確認してください。
Docker engine と Compose plugin のインストール
Ubuntu 標準の docker.io パッケージや、古いスタンドアロンの docker-compose Python バイナリではなく、Docker 公式の apt リポジトリを使用してください。最新の 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 versiondocker compose version を実行し、Docker Compose version v2.x のような出力が得られれば正常です。もし docker: 'compose' is not a docker command というエラーが出る場合は、plugin のインストールに失敗しています。このまま進めると後で原因不明のエラーが発生するため、ここで修正してください。
compose ファイル: シングルノードの replica set としての MongoDB
この部分は間違いやすいため、注意深く読んでください。Rocket.Chat は、接続中のクライアントに新しいメッセージをリアルタイムで送信するために MongoDB の change streams を使用します。change streams は replica set でのみ利用可能です。Rocket.Chat の接続先に通常の standalone な mongod を指定すると、接続はされますが change stream のオープンに失敗し、無限に再起動を繰り返します。解決策は簡単です。通常の MongoDB コンテナを実行し、--replSet を付けて起動してから、1つのメンバーからなる set を初期化します。
作業ディレクトリと 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 のポートは 0.0.0.0 ではなく 127.0.0.1:3000 に公開されています。アプリ自体には TLS がないため、同じホスト上の reverse proxy からのみアクセスできるようにします。すべてのインターフェースにバインドすると、平文のログインページがそのまま公開インターネットに露出してしまいます。MongoDB はホストには公開されていません。Compose の内部ネットワーク経由で、mongodb という名前でのみアクセス可能です。これは MONGO_URL が使用する hostname と一致します。MONGO_URL には ?replicaSet=rs0 が含まれています。これがないと、たとえ replica set であってもドライバーはサーバーを standalone として扱い、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 フラグにより、そのリリースが管理の手間を減らしたいサーバーに適した long-term-support ビルドであるかを確認できます。
レプリカセットの初期化
スタックを起動します:
sudo docker compose up -dRocket.Chatはすぐにクラッシュし、Dockerは再起動を繰り返します。これは想定通りの動作です。まだレプリカセットが存在しないためです。手動で一度だけ作成してください:
sudo docker compose exec mongodb mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "mongodb:27017"}]})'正しい結果は{ ok: 1 }です。数秒以内に単一のノードがプライマリに選出されます。以下のコマンドで確認してください:
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と一致する明示的なサービス名を使用して初期化してください。
初回起動:起動プロセスを確認する
Replica setがprimaryになると、Rocket.Chatの次回の再起動は正常に接続され、初回実行用のmigrationを開始します。ログを確認してください:
sudo docker compose logs -f rocketchat待機すべき行は、起動バナーです:
+--------------------------------------------+
SERVER RUNNING
Rocket.Chat Version: 8.5.1
NodeJS Version: 22.22.3 - x64
+--------------------------------------------+初回起動は時間がかかります。アプリがdatabase migrationとindexの作成を行うため、1〜2分は待機してください。もしログにMongoServerSelectionError: Server selection timed out after 30000 ms(topology type: ReplicaSetNoPrimary)が繰り返し表示される場合は、replica setが初期化されていません。もしランダムなhashに対してgetaddrinfo ENOTFOUNDが繰り返し表示される場合は、誤ったhostで初期化されています。どちらの場合も、前のステップに戻ってください。SERVER RUNNINGが表示されたら、Rocket.Chatは127.0.0.1:3000で待機状態にあります。次に、実際のhostnameとTLSを設定してください。
TLSを適用する
Rocket.Chatを平文のHTTPで公開しないでください。http://経由でログインすると、経路上の第三者に管理者パスワードを渡すことになります。同じサーバー上のリバースプロキシでTLSを終端し、127.0.0.1:3000へ転送してください。重要な点が2つあります。まず、Rocket.Chatはリアルタイム通信を行うため、プロキシはWebSocketのupgradeヘッダーを転送する必要があります。次に、コンテナのROOT_URLは、ユーザーが入力する公開HTTPSアドレスと完全に一致していなければなりません。
まず、アプリへプロキシし、upgradeヘッダーを転送する標準的なHTTP nginxサーバーブロックを作成します。これを/etc/nginx/sites-available/rocketchatとして保存し、sites-enabledへシンボリックリンクを作成して、以下を実行してください。
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への自動リダイレクトが追加されます。また、更新も自動化されます。1つのプロキシの背後で複数のコンテナを運用している場合は、多くのDockerアプリに自動TLSを適用するTraefik の方が効率的です。rocketchatサービスにrouterとserviceのlabelを追加すれば、Traefikがリクエストを処理し、証明書を自動更新します。この場合、nginxのブロックは不要です。いずれの方法でも、compose.yml内のROOT_URLをhttps://chat.example.comに設定し、sudo docker compose up -dを再実行してコンテナに変更を反映させてください。サーバーをパブリックインターネットではなく、自身のネットワーク内からのみアクセス可能にしたい場合は、VPS上で自己ホストするWireGuard VPN を導入し、プロキシをトンネルアドレスにバインドしてください。
初回セットアップウィザード
https://chat.example.com にアクセスすると、Rocket.Chat の短いウィザードが開始されます。まず、admin account(管理者アカウント)を設定します。氏名、username、email、および強力な password を入力してください。これが唯一の既存アカウントになるため、情報を紛失しないでください。次に、organisation and server info(組織およびサーバー情報)を設定します。name、industry、size、site name、および default language を入力してください。これらは外観の設定であり、入力後に次へ進みます。最後に、重要な選択を行います。Rocket.Chat Cloud に register this workspace(ワークスペースを登録)するか、standalone(スタンドアロン)として運用するかを選択します。
登録を行うと、Rocket.Chat の gateway を介した mobile push notifications と add-on marketplace が利用可能になります。ただし、Rocket.Chat の cloud と control-plane の関係性が構築されます。standalone の場合、サーバーは完全にプライベートで依存関係のない状態が保たれますが、iOS および Android への push notifications は機能しなくなります。これは、Apple と Google が自作アプリに push certificates を保持することを許可していないためです。公式アプリは cloud gateway を経由して動作します。プライバシーを最優先し、ユーザーが web app を使用する場合は standalone を選択してください。mobile push が必須の場合は、registration を選択してください。設定は後で Admin から変更可能です。
招待する前に制限をかける
Rocket.Chatはデフォルトでopen registration on(公開登録が有効)になっています。Registration FormがPublicに設定されているため、URLを知っている人は誰でもアカウントを作成できます。パブリックなホスト名を使用している場合、これはセキュリティ上のリスクとなります。Admin → Settings → Accounts → Registration に移動し、Registration Form を手動作成または招待リンクによる作成を行う Disabled、あるいは Secret URL に設定してください。また、公開の読み取り専用チャンネルが必要な場合を除き、Allow Anonymous Read と Allow Anonymous Write はオフにしてください。
アップロード先の保存先も決定してください。デフォルトの 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この単一の gzipped アーカイブに、ユーザー、チャンネル、メッセージ、設定、および GridFS にアップロードがある場合はファイルもすべて含まれます。アップロード先を filesystem や S3 に変更している場合は、そのストレージを別途バックアップしてください。新しいスタックにリストアするには、まず replica set を初期化した後、以下の手順を行ってください。
sudo docker compose exec -T mongodb mongorestore --archive --gzip --drop < rocketchat-2026-07-15.archive.gzアーカイブは必ず サーバーの外(オブジェクトストレージや別のサーバーなど、VPS の消失時にバックアップも失われない場所)へコピーしてください。また、cron を使用して毎晩ダンプを実行してください。リストアのテストを行っていないバックアップは、バックアップではなく単なる「期待」に過ぎません。実際に必要になった時に備え、使い捨ての VPS で一度リストアの練習を行ってください。
Upgrades: pin tags, read the notes, respect the Mongo matrix
アップグレードを確実に成功させるための2つのルールがあります。1つ目は、Rocket.Chatを一度に1つのメジャーバージョンずつアップグレードすることです。Rocket.Chatは起動時にスキーマのマイグレーションを実行します。メジャーバージョンを飛ばしてアップグレードすることはできません。例えば、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 は動作していますが、ドライバーが primary を選択できません。エラー文字列を確認することで、原因を特定できます。topology type が ReplicaSetNoPrimary の Server selection timed out after 30000 ms は、rs.initiate() が実行されていないことを意味します。つまり、セットにまだ設定がありません。getaddrinfo ENOTFOUND の後にランダムなハッシュが続く場合は、host: "mongodb:27017" を指定せずに初期化したことが原因です。この場合、MongoDB は解決不可能なコンテナの hostname を通知します。sudo docker compose exec mongodb mongosh --eval 'rs.status()' で診断してください。MongoServerError: no replset config has been received エラーが出る場合は、セットを初期化してください。name がランダムなハッシュであるメンバーが表示される場合は、service name を使用して再初期化してください。
Web UI は読み込まれますが、ログイン中のまま処理が終わりません。 ブラウザのコンソールを開くと WebSocket connection to 'wss://chat.example.com/websocket' failed が表示されます。これは、ほとんどの場合 ROOT_URL の不一致、または proxy が upgrade headers を転送していないことが原因です。ROOT_URL が https:// を含む正確な公開アドレスと一致しているか確認してください。また、nginx の location ブロックで Upgrade と Connection "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 を追加し、command 内の --wiredTigerCacheSizeGB 1 で MongoDB の cache を制限してください。ただし、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 の proxy_pass をそれに合わせて更新してください。
FAQ
Does Rocket.Chat really need a MongoDB replica set?
Yes, even for a single server with one database node. Rocket.Chat delivers messages in real time using MongoDB change streams, and change streams are a replica-set-only feature — a standalone mongod cannot open one. You do not need multiple machines; you run one MongoDB container started with --replSet rs0 and initialise a one-member set with rs.initiate(). Skip that step and the driver never finds a primary, so Rocket.Chat restart-loops with MongoServerSelectionError: Server selection timed out and never finishes booting.
How much RAM does self-hosted Rocket.Chat need?
Plan for 4 GB as the practical minimum and 8 GB for a busy team. Rocket.Chat's Node process uses about 1 to 1.5 GB and MongoDB claims roughly half the remaining RAM for its WiredTiger cache, so on a 2 GB box the two collide and the out-of-memory killer terminates mongod under any real load, showing Killed in the logs and exit code 137. Two GB is only enough to evaluate the software with a couple of test users.
How do I put Rocket.Chat behind HTTPS?
Run a reverse proxy on the same VPS that terminates TLS and forwards to 127.0.0.1:3000, and set the container's ROOT_URL to your public https:// address. The proxy must forward the WebSocket upgrade headers or login will hang. Certbot with nginx is the simplest single-app setup; Traefik is cleaner if you run several containers behind one proxy and want automatic certificate management.
How do I back up a self-hosted Rocket.Chat?
Take a consistent database dump with mongodump rather than copying the volume: docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz. That archive contains users, channels, messages, and settings, plus uploaded files if you left storage on GridFS. Copy it off the server, automate it nightly with cron, and rehearse a mongorestore on a throwaway box so you know the restore actually works.
How do I upgrade Rocket.Chat without breaking MongoDB?
Upgrade Rocket.Chat one major version at a time — it runs migrations on boot and refuses to skip majors — and read each release's notes before bumping the pinned image tag. Check which MongoDB versions your target release supports with curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions, and when you move MongoDB, step one major at a time and set setFeatureCompatibilityVersion with confirm: true after each hop. Always take a mongodump first.