COMPOSE_PROJECT_NAME · 独立したデータボリュームとポート表 · env_file とコンテナ内 env の突き合わせ
同一台のVPS上で二つのOpenClawを並行稼働させるとき、もっとも多いトラブルはポートの衝突、デフォルトのネットワーク名やボリューム名の衝突、そしてホストには API キーがあるのにコンテナの env が空であるという組み合わせです。本文ではシングルインスタンス基準とマルチインスタンスで変える最小差分の対照表で COMPOSE_PROJECT_NAME、データディレクトリ、ポート表を整理し、env_file と environment の注入経路を説明したうえで、六段階の確認手順とシングルへ戻す条件を示します。Docker Compose 本番基準の記事およびExit 137 と立ち上げ時のトラブルシュート記事とあわせて読むと流れがつながります。
docker-compose.yml を複製してポートだけ変えても、それだけでは分離になっていません。デフォルトのプロジェクト名のままだとネットワークや匿名ボリュームが衝突します。ホストの export は自動ではコンテナに入りません。リバースプロキシの upstream は古いコンテナ IP を指したまま残ることがあります。次のような兆候が出たら、対照表の項目を順に確認し、コンテナを増やす前に原因を切り分けます。
ポートだけ変えて COMPOSE_PROJECT_NAME を変えていない:デフォルトのブリッジネットワークと内部 DNS が、もう一つのスタックのコンテナ名を解決してしまうことがあります。
データディレクトリのシンボリックリンクやマウントの重なり:二系統の ~/.openclaw やワークスペースが実際には同じディスク上の接頭辞を共有しており、アップグレードスクリプトが互いの状態を上書きします。
API キーがシェルのプロファイルにしかない:docker compose up の子プロセスが、compose が期待する env_file のパスを読めていません。
healthcheck の猶予が短すぎる:二番目のインスタンスはコールドスタートが長く、オーケストレータが異常と判断して再起動を繰り返し、一つ目のインスタンスとロックを奪い合います。
リバプロが 127.0.0.1 の古いポートを向いたまま:二つの Gateway を切り替えても、エッジ側のトラフィックがすでに停止したインスタンスへ流れ続けます。揃え方はリバプロと TLS の記事を参照してください。
次の列は、同一ホストに二スタックを置くときにいちばんよく触る「最小差分」です。短期の検証用に副系だけ立てる場合でも、自動化の前にプロジェクト名、データディレクトリ、ホスト側ポート、env_file のパスの四つは必ず分けます。リソース上限とログローテーションは本番基準の記事に従います。
| 項目 | シングル基準 | マルチで必ず分ける | よくある落とし穴 |
|---|---|---|---|
COMPOSE_PROJECT_NAME | ディレクトリ名のデフォルト | スタックごとの短い一意名(例 oc_a / oc_b) | フォルダ名だけ変えてプロジェクト名が同じまま匿名ボリュームを共有する |
| データボリュームのパス | 単一のホストバインド | /srv/openclaw-a と /srv/openclaw-b を完全に分離 | 子パスのシンボリックリンクが同じ親ディレクトリを指す |
| ホスト側ポート | 単一の 3000:3000 | 空いているホストポートに写し、ポート表に記録する | 二番目のスタックの起動に失敗しても systemd が再起動を繰り返す |
| 環境の注入 | 単一の .env | スタックごとに .env.oc-a を用意し compose で env_file を明示する | ホストの export が compose の解釈範囲に含まれていない |
| ヘルスチェック | 単一の start_period | コールドスタートが長い場合は start_period と retries を増やす | 二スタックが同時に再起動して CPU が尖る |
プロジェクト名とデータディレクトリを先に決め、ポート表はそのあとです。順序が逆だと、はじめてアップグレードスクリプトを流したタイミングで二系統の状態が同じ接頭辞に書き込まれます。
ここでは、公式またはチームの compose で単一ホストの起動まで済んでいる前提です。二番目のスタックについて、レンダリング後の設定と実行時の envを監査できる状態にすることが目的です。
プロジェクト名とディレクトリを書き込む:COMPOSE_PROJECT_NAME をエクスポートし、独立したデータディレクトリを作成し、入れ子のシンボリックリンクがないことを確認します。
ポート表を固定する:Gateway、コントロール UI、追加チャネル用に空いているホストポートを割り当て、運用表に記録します。
env_file を分ける:スタックごとに秘密ファイルのパスを持たせ、.env を共有しません。docker compose --project-directory で作業ディレクトリを明示します。
レンダリングで確認する:docker compose -f ... config を実行し、二スタックの networks、volumes、ports に同名のマウントが残っていないか比較します。
実行時に検証する:docker compose exec でコンテナに入り、対象の変数を表示し、ホスト側でマスクした grep の結果と一致するか確認します。
ヘルスとログ:基準記事に沿って start_period と json-file の上限を設定し、二スタックが同時にディスクを埋めないようにします。
export COMPOSE_PROJECT_NAME=oc_b docker compose --env-file ./.env.oc-b -f docker-compose.yml config > /tmp/oc-b.rendered.yml docker compose --env-file ./.env.oc-b -f docker-compose.yml up -d docker compose --env-file ./.env.oc-b exec -T openclaw-gateway sh -lc 'env | grep -E "ANTHROPIC|OPENAI" | sed "s/=.*/=***MASK***/"'
補足:config サブコマンドは合成結果だけを解釈し、コンテナは起動しません。レンダリング結果の差分を保存してから up すると、一つ目のスタックとの突き合わせがしやすくなります。
次の項目はオンコールと事後レビューの出発点です。実際の compose 断片とホストの監視しきい値に置き換えて使い、対外向けの SLA であるかのように書かないでください。「たまに別インスタンスにつながる」事象では、少なくとも二スタックのレンダリング結果、docker inspect のネットワーク、リバプロの upstream のスナップショットを同時に保存します。
up する前に ss -lntp または同等のコマンドを実行し、出力をチケットの添付に残します。json-file の書き込みが二倍になると、inode と帯域が CPU より先に限界になります。巡回の間隔は基準記事の節を参照してください。| 症状 | まず疑う項目 | 推奨コマンドまたは証跡 |
|---|---|---|
| コンテナ内に API キーがない | env_file のパス、compose の作業ディレクトリ | docker compose config と exec での env の突き合わせ |
| ヘルスチェックが不安定で再起動する | start_period、CPU steal | ホストの dmesg、cgroup の統計、コンテナログの時系列 |
| リクエストが別インスタンスに届く | リバプロの upstream、古いコンテナの残骸 | リロード前後の docker ps -a と upstream 設定の diff |
注意:一つ目のスタックを止めないまま同じパスで .env を上書きすると、二系統のキー交代の順序が崩れます。必ず新しいファイルをコピーしてから参照を切り替えてください。
メモリの小さい VPS に本番相当の OpenClaw を二つ詰め込むと、しばしばディスクとログで先に負けます。独立したデータディレクトリとポート表がなければ、障害対応は「どの Gateway に届いているか当てる」作業に戻ります。compose を手でいじるだけでレンダリング差分を残さないと、アップグレード後に「誰がどの環境変数を変えたか」を説明できなくなります。
一方で、安定したホスト、帯域の見通し、分離できる作業ディレクトリが必要なチームでは、OpenClaw を契約可能なクラウド Mac や専用 VPS プランの上で動かしたほうが、このチェックリストを実行しやすいです。マルチの検証と本番を同じ運用の物語の中で分けたい場合は、ノード役割を分けられ、経路を監査できるVpsMesh の Mac mini クラウドレンタルのほうが向くことが多く、compose の差分セットとキュー方針も同じ粒度で受け入れ基準にできます。
本文の対照表に沿って、匿名ボリューム、デフォルトのネットワーク名、ホスト側ポート、env_file が一つ目のスタックとまだ重なっていないか確認してください。ディレクトリ名だけ変えてプロジェクト名を変えていないパターンがもっとも多いです。healthcheck と restart の細部はCompose 本番基準の記事を参照してください。
compose が正しい env_file を読んでいない、作業ディレクトリがずれている、変数が build ブロックにしかない、といった場合が多いです。本文の六段階で docker compose config と exec の出力を揃えてください。立ち上げ時はExit 137 とトラブルシュートの記事の環境変数の節も参照してください。