OpenClaw は入れたが、Gateway をパブリックインタフェースに直結させたくない開発者は、Ubuntu 24.04 系 VPS で待ち受けアドレスの選び方、systemd による予測可能な再起動、Cloudflare Tunnel(または同等)でループバックだけをホスト名の背後に出す方法でつまずきがちです。本稿は Docker 本番向け記事・マルチプラットフォーム導入記事を補う ベアメタル+ systemd +トンネル の道筋として、環境の基準、Gateway 設定の要点、Unit の例、ingress と症状別の切り分けを整理します。安定したリモート Mac と組み合わせたよくある役割分担にも触れます。
マルチプラットフォームの記事は「まず動かす」入口です。Docker 本番向けの記事は、すでにコンテナ標準があり Compose やオーケストレーションで揃えたいチーム向けです。本稿は、ベアメタルの Ubuntu 24.04 で抽象層を最小にし、systemd でクラッシュ復旧と起動時自動起動を担い、Cloudflare Tunnel(または自前のリバースプロキシ)で TLS とアクセス制御をアプリの外に出したい方向けです。三つは排他ではありません。まずベアメタルで固めてからコンテナ化しても構いません。
次の 6 点はサポートで多い認識のずれです。ここを揃えてから後段のチェックリストを使ってください。
「curl が通る」を「本番として安全」とみなす:0.0.0.0 で待ち受け、前段プロキシなしだと認証と TLS がアプリのデフォルトに依存します。設定がずれると広範なスキャン対象になります。
Node ランタイムと glibc の結びつきを軽視する:Ubuntu 24.04 で古いプリビルドや NODE_OPTIONS の誤りがあると、手元では動くが systemd では立ち上がらない、という見かけが出ます。
作業ディレクトリと権限を /root に雑に置く:アップグレードやログローテーションで権限が変わると設定読み込みに失敗し、systemd が連続再起動します。ログも埋もれがちです。
ヘルスチェックがプロセスの有無だけ:HTTP の準備完了や上流モデルへの到達を見ないと、LB には流れ続け、トンネル側は 502 のままになり、調査の層を誤ります。
トンネルは生きているがポートが合わない:cloudflared の ingress と Gateway の実際の待ち受けがずれると、DNS は解けるのに正しいループバックに届きません。
Linux ゲートウェイを万能ワーカーにする:Xcode やシミュレータ、Apple Silicon 固有の処理を VPS に無理に載せると、「OpenClaw が不安定」に見えますが、層の置き場の問題です。
「ループバック待ち受け+トンネル」が自分の前提に合うか判断できたら、続く環境基準と手順に進んでください。
導入の速さだけでなく、攻撃面・可観測性・ロールバックを同じ行で比較してください。表はレビュー用のたたきです。ポートやイメージタグは自環境の値に差し替えてください。
| 観点 | Gateway を公開インタフェースに | Docker / Compose 本番 | systemd + ループバック + Tunnel |
|---|---|---|---|
| 露出 | 最大。TLS と FW はアプリ層任せになりがち | publish ポートとネットモード次第。host ネットの誤設定に注意 | プロセスは 127.0.0.1 のみ。外向きはトンネル/エッジ |
| 証明書 | 自前 ACME または手動ローテーション | Traefik / Caddy / クラウド LB を前段に置くことが多い | Cloudflare エッジで TLS 終端(または同等) |
| 再起動・調査 | 外部デーモンや手作業に依存 | restart ポリシーやログドライバを別設計 | systemd の再起動と journal の追いやすさ |
| 向くチーム | 最小 PoC。本番非推奨 | コンテナ規約とイメージ供給がある組織 | 単一 VPS でスクリプト再現性を重視する中小チーム |
本番ゲートウェイで問うべきは「届くか」ではなく「どの経路で誰が許可されるか」です。待ち受けをループバックに絞ることで、その問いをエッジ側へ移せます。
Ubuntu 24.04 LTS を例にすると、現行の Node.js LTS メジャー(2026 年初頭時点では多くの場合 22 系。正確には nodejs.org に従ってください)に合わせます。nvm、fnm、NodeSource のいずれでも構いませんが、本番では マイナー版を固定し、アップグレードの窓を記録してください。ディストリビューション付属の node だけでは足りないことが多いです。
ファイアウォールの基準:SSH は鍵認証、ufw は既定で着信拒否、Gateway 用の業務ポートに 公開ルールを開けない(外向きはトンネル経由のみ)。直接デバッグが必要な場合は時間限定の許可や踏み台を使い、変更記録に解除時刻を残してください。
専用ユーザーとディレクトリを用意する:例としてユーザー openclaw、実行状態は /var/lib/openclaw、設定は /etc/openclaw(ファイル 640、ディレクトリ 750)。root 配下に混ぜないでください。
Node の版とロックファイルを固定する:リポジトリまたはデプロイ先に package-lock.json または pnpm-lock.yaml を残し、本番は npm ci 等でインストールします。インストール時にメジャーが上がらないようにします。
glibc とネイティブ拡張を確認する:ネイティブモジュールを使う場合は対象マシンでクリーンインストールとスモークを一度走らせ、dlopen エラーがないことを確認します。
タイムゾーンを揃える:timedatectl を UTC またはチーム標準にし、トンネル/エッジのログと突き合わせやすくします。
ufw の最低限を有効にする:ufw allow OpenSSH のあと ufw enable。業務ポートは閉じたまま、ss -tlnp で外向きに Gateway が見えていないことを確認します。
ヘルスチェック用エンドポイントを決める:トンネルを掛ける前に curl -fsS http://127.0.0.1:PORT/health(パスは設定に合わせる)を Runbook に書き、ExecStartPost や外部プローブと一致させます。
補足:OpenClaw のサブコマンドや設定ファイル名は公式ドキュメントに従ってください。以下の Unit と YAML のパス・ポートは置き換え前提の例です。実際の配置に合わせてから有効化してください。
待ち受け:本番では 127.0.0.1:PORT を推奨します。対応していれば Unix ドメインソケットも併用できます。トークン:環境ファイルで注入し、world-readable な設定に書かないでください。ローテーションは新トークンを書き込み、ローリング再起動のあと旧トークンを削除します。ヘルス:プロセス生存に加え、HTTP の準備完了またはモデル制御面への到達のいずれかを必ず含めます。そうしないとトンネルは「届かない」だけで「層が違う」ことは教えてくれません。
[Unit] Description=OpenClaw Gateway (loopback) After=network-online.target Wants=network-online.target [Service] User=openclaw Group=openclaw WorkingDirectory=/var/lib/openclaw EnvironmentFile=-/etc/openclaw/gateway.env # ExecStart は公式ドキュメントの実コマンドに差し替えてください ExecStart=/usr/bin/node /opt/openclaw/gateway.mjs --config /etc/openclaw/gateway.yaml Restart=on-failure RestartSec=5 LimitNOFILE=1048576 # ログは journal を優先。ファイルにする場合は logrotate と権限を設定 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target
起動の順序の例:systemctl daemon-reload → systemctl enable --now openclaw-gateway → systemctl status で主プロセスが安定するまで確認 → ローカルで curl ヘルス → その後 cloudflared を起動します。
tunnel: <YOUR_TUNNEL_ID>
credentials-file: /etc/cloudflared/<YOUR_TUNNEL_ID>.json
ingress:
- hostname: claw.example.com
service: http://127.0.0.1:8787
- service: http_status:404
注意:トンネル認証 JSON は root または専用ユーザーだけが読めるようにします。イメージレイヤや公開 CI ログに埋め込まないでください。自前の Nginx/Caddy で TLS を終端する場合も、エッジで TLS、上流はループバックのみ、という形を維持してください。
ログ確認の順序(切り分け時):① journalctl -u openclaw-gateway -b --no-pager でクラッシュループと環境変数の読み込みを確認する。② アプリ独自のログディレクトリ(あれば)。③ journalctl -u cloudflared または同等のトンネルサービス。④ 最後に curl -v をローカルと外部の両方で実行し、プロセス問題とルーティング問題を分けます。
ポート:EADDRINUSE のときは ss -tlnp で占有プロセスを確認し、ゾンビでないことを確かめます。ポートを変えたら ingress と内部プローブも合わせます。権限:EACCES では設定と鍵の owner/group を確認し、systemd の User が読めることに揃えます。トンネル:外部が 502 でローカル curl は成功するときは、ingress、Cloudflare への接続、DNS のゾーンを順に確認します。モデル接続:タイムアウトや 401 がログに出る場合は、サーバ上からベンダーのベースライン API を curl し(シークレットをシェル履歴に残さない)、出口 IP ブロックとトークン期限切れを切り分けます。
よくある分離は次のとおりです。Linux VPS に外向き Gateway・キュー・軽いオーケストレーションを置き、安定したリモート Mac(物理の Apple Silicon)に Xcode ビルド・シミュレータ・macOS 専用ツールチェーンを載せます。重い処理は Mac に振り分けることで、「ゲートウェイを公開ポートに出さない」方針を保ちつつ Linux に不向きな負荷を避けられます。Mac を契約可能な実行ノード、Linux を自管のコントロールプレーンとみなし、SSH・キュー・プライベートトンネルで接続します。障害ドメインを分けることが重要です。ゲートウェイが落ちても Mac 上のビルドキャッシュが無意味にならないようにし、Mac メンテナンスで API 入口全体が止まらないようにします。
127.0.0.1 と単一ポートに固定し、IPv6 全インタフェースへの誤バインドを避けます。Restart=on-failure と RestartSec=5 でログが読みやすくなります。必要なら StartLimitIntervalSec を追加します。localhost ではなく http://127.0.0.1:PORT と明示し、IPv6 の ::1 に解決されてアプリが IPv4 のみのときの不整合を減らします。Linux VPS とトンネルだけの構成はコントロールプレーンと API の集約には向きますが、iOS CI/CD、シミュレータ、Metal、Xcode チェーンでは壁に当たります。リモートヘッドレス環境の互換性を妥協するか、イメージと運用コストを積み上げるかの二択になりがちです。重い処理を専用ラインとメンテナンス窓が取りやすい Apple Silicon のリモート Macに置き、ゲートウェイは慣れた systemd とトンネルのままにすると、影響範囲が把握しやすくなります。エージェントやビルドパイプラインを長期安定で回したいチームにとって、NodeMini の Mac mini クラウドレンタルは多くの場合より妥当な選択です。コントロールプレーンは自管 VPS に残し、実行面は Apple エコシステムに沿った機材と契約期間に任せられます。
本番ではループバックのみの待ち受けを強く推奨します。TLS とアクセス制御はトンネルまたはリバースプロキシに任せてください。公開ポートが必要な場合はホワイトリスト・レート制限・監査が必要になり、多くの場合トンネルより複雑です。関連記事は OpenClaw コラム を参照してください。
サーバ上でループバックへ curl し、Gateway が本当に準備できているか確認します。次にトンネルの ingress と認証情報を確認し、最後に上流モデルや DNS を疑います。実行ノードを契約する前に レンタル料金 で期間とリージョンを揃えてください。