Claude Media
Claude Codeセルフホスト環境のCLIフラグ・メトリクスリファレンス

Claude Codeセルフホスト環境のCLIフラグ・メトリクスリファレンス

self-hosted-runnerとorchestratorのCLIフラグ・環境変数・Prometheusメトリクスを、やりたいことから引ける形に並べ替えたリファレンスです。

この記事でわかること

Claude Codeのセルフホスト環境は、self-hosted-runnerと、オンデマンドRunnerを起動するorchestratorの2プロセスで動きます。公式のリファレンスはフラグをアルファベット順に並べた一枚表ですが、運用中に見返すときは「今どのフラグを調整すべきか」から逆引きしたい場面がほとんどです。本記事は、フラグ・環境変数・Prometheusメトリクスを目的別に並べ替え、実務で迷いやすい単位換算とカウンター定義の落とし穴を添えます。全項目の網羅ではなく、運用でよく触る範囲に絞っています。バージョンごとの新規フラグやKubernetes・Docker Composeの構成例はClaude Codeセルフホスト環境の本番運用で扱っています。

やりたいことから引く早見表

やりたいことフラグ / 変数補足
アイドルRunnerを自動で畳みたいフラグ / 変数--exit-if-unused-min補足一度も仕事を受け取らないまま指定分ポーリングし続けたら終了。オートスケーラーの縮小に使う
詰まったセッションを強制終了したいフラグ / 変数--kill-session-after-min補足ターン開始から壁時計でN分経過したセッション子プロセスを終了する安全弁
ホストの停止猶予より短い時間で確実にRunnerを畳みたいフラグ / 変数--retire-at補足絶対Unixタイムスタンプで退役時刻を指定。スポットインスタンスの回収などシグナルなしでホストが消える場合に使う
Runnerイメージのバージョン差を検知したいフラグ / 変数claude_code_self_hosted_runner_infoメトリクス補足ラベルにバージョンを持つ常時1のゲージ。count by (version)で混在を検出できる
キューの詰まりでオートスケールしたいフラグ / 変数claude_code_self_hosted_orchestrator_pool_pending_sessions補足Environment全体の待機セッション数。queue_pending_sessionsではなくこちらをスケーラーに渡す
Runnerが1台も生きているか監視したいフラグ / 変数/healthzlast_poll_age_ms補足増え続けたらポーリングループが詰まっているサイン

Runnerの主要CLIフラグ

大半のフラグには対応する環境変数があります。両方指定した場合はフラグが優先されます。まず押さえておきたいのは単位の癖です。CLIフラグの時間指定は分または秒、対応する環境変数は常にミリ秒(_MSサフィックス)です。--exit-if-unused-min 10SELF_HOSTED_RUNNER_IDLE_SHUTDOWN_MS=600000は同じ意味ですが、HelmのvaluesファイルなどでSELF_HOSTED_RUNNER_STARTUP_TIMEOUT_MS: "15"と書くと、既定値の15分ではなく15ミリ秒になります。

セッションごとの使い捨てコンテナ構成で、アイドル10分での自動終了とリポジトリ設定ガードを有効にして起動する例です。

claude self-hosted-runner \
  --environment-secret-file /etc/claude/environment-secret \
  --capacity 1 \
  --exit-if-unused-min 10 \
  --confine-repo-settings enforce

稼働終了とドレインを制御するフラグ

フラグ既定内容
--drain-grace-sec既定0内容0ならアクティブセッション終了と同時に即終了。正の値ならその秒数だけロックされたオーナーのキューを再ポーリングし続ける
--drain-wait-sec既定0内容ドレイン開始後、進行中のターンとバックグラウンドタスクの完了をN秒待ってから子プロセスを終了する
--defer-shutdown-max-min既定0内容最初のSIGTERM/SIGINTではドレインせず、既存セッションを維持し続けてN分後に強制解放する
--release-idle-session-min既定0内容ターン完了後またはユーザー操作待ちの状態がN分続いたセッションのスロットを解放する
--kill-session-after-min既定0内容セッション子プロセスの壁時計寿命の上限。詰まったセッションを止める最後の砦
--retire-at <epoch秒>既定未設定内容絶対時刻での退役。シグナルなしでホストが消えるインフラ向け

--drain-grace-sec0から動かすかどうかは、本番運用のハードニングで扱っているセッションごとの使い捨てコンテナ構成と表裏の関係です。0のままなら1コンテナ1セッションが保たれ、正の値にするとコンテナの再利用と引き換えにファイルシステム分離が弱まります。

ネットワークと認証まわりのフラグ

フラグ既定内容
--proxy-authorization-command <command>既定未設定内容egressプロキシへの接続ごとにシェルコマンドを実行し、標準出力をProxy-Authorizationヘッダーの値にする
--proxy-authorization-file <path>既定未設定内容同じ用途をファイル読み取りで行う。別プロセスがトークンをその場でローテーションする構成に向く
--git-host-rewrite <from>=<to>既定未設定内容クローン前にhttps://<from>/...https://<to>/...へ書き換える。分割ホライズンDNS向け
--git-ssh-rewrite <host>既定未設定内容クローン前にHTTPS URLをSSH URLへ書き換える。SSH専用のgitホスト向け
--use-anthropic-git-proxy既定off内容git通信をAnthropicのプロキシ経由にする。--capacity 1とgit 2.32以降が必須で、書き換え系フラグより優先される

容量とファイルシステムのフラグ

フラグ既定内容
--capacity <n>既定1内容Runner 1台が同時に処理するセッション数の上限。同じEnvironment内では全Runnerで揃える
--base-dir <path>既定/workspace(Windowsは既定なし)内容リポジトリのチェックアウト先。v2.1.225以降は起動時にディレクトリ作成を試み、書けない場合は起動自体が失敗する(それ以前は最初のセッション開始時にしか検知できなかった)
--startup-timeout-min <n>既定15内容子プロセスが初期化シグナルを送るまでの待ち時間。過ぎるとセッションスロットを解放する
--client-label <label>既定ホスト名内容Runner登録時に送るラベル。claude_code_self_hosted_runner_infoメトリクスのclient_labelラベルにも出る(v2.1.248以降)

セキュリティと監査のフラグ

フラグ既定内容
--confine-repo-settings <mode>既定warn内容リポジトリのコミット済み設定がセッション外への書き込み・読み取りやサンドボックス無効化を試みたときの挙動。warnはログのみ、enforceは起動拒否、offはスキャン自体を無効化
--trust-workspace [bool]既定on内容セッションごとのリポジトリパスに永続的な信頼を事前付与し、コミット済みのpermissions.allowadditionalDirectoriesを有効にする
--lock-to-account <id>既定未設定内容起動時点でRunnerを特定アカウントに事前ロックする。最初のセッションでロックする既定動作を上書きする
--debug-token-dir <path>既定未設定内容実行中のトークンをディスクへ書き出す。デバッグ専用で本番では使わない

--confine-repo-settings--trust-workspaceの組み合わせは、本番運用のハードニング項目とそのまま対応します。ここでの値の選び方に迷ったら、まずそちらの8項目チェックリストから読むと文脈がつかみやすくなります。

orchestratorの主要CLIフラグ

オンデマンドRunner構成を使う場合、orchestratorサブコマンドを使います。--api-url--environment-secret-file--hooks-dir--health-port--log-levelはRunnerと同じ既定値で受け取り、加えて次の専用フラグを持ちます。

フラグ既定内容
--hook-concurrency <n>既定4内容同時実行するspawn-runnerフックの上限。1ポールで確保する要求数の上限でもある
--hook-timeout <sec>既定60内容フックのプロセスツリーを強制終了するまでの秒数。5秒のkill猶予を足した値が--expected-spawn-secondsを下回っている必要があり、超えているとorchestratorが起動時に拒否する
--expected-spawn-seconds <sec>既定120内容スポーンしたRunnerの起動p99時間の見込み。サーバー側のリース期間として毎ポールで送られる。サーバー強制レンジは10〜3600秒で、全レプリカで同値にする必要がある
--min-idle <n>既定0内容待機用のRunnerを事前に立てておく数。--exit-if-unused-minと組み合わせて余剰分を自動的に畳む
--debug-dir <path>既定未設定内容スポーン要求のwork orderとフックのstderrをディスクに書き出す。デバッグ専用

--expected-spawn-secondsは、Runnerの実際の起動時間と乖離すると誤動作します。短すぎるとまだ起動中のセッションが別のRunnerへ再割り当てされ、長すぎるとオートスケールの反応が遅れます。時間系フラグには32ビットタイマー上限に由来する上限もあり、--*-min系フラグは10080分(7日)、--drain-grace-secは604800秒(7日)、--drain-wait-secは86400秒(24時間)が上限です。

環境変数だけで指定する設定

CLIフラグを持たず、環境変数でのみ調整する項目です。既定を変える機会はそう多くありませんが、障害調査で存在を知らないと原因にたどり着けません。

環境変数既定内容
SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS既定30000内容バックグラウンドタスク完了後、その結果を読む後続ターンが始まるまでセッションを「稼働中」とみなす猶予
SELF_HOSTED_RUNNER_HOST_CONFIG_DIR既定~/.claude内容Runner起動時にスナップショットし各セッションのCLAUDE_CONFIG_DIRへ渡すディレクトリ
SELF_HOSTED_RUNNER_SIGKILL_GRACE_MS既定30000内容I/O待ちで固まった子プロセスへのSIGKILL到達を待つ時間。既定値より下は効かない。--post-session-hook-timeout-sec+15秒が下限で、--push-outcome-on-release設定時はさらに+30秒。既定構成では実効75秒
SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS既定900000内容--kill-session-after-minによるkillがターン完了待ちで繰り延べられる場合の上限
CLAUDE_RUNNER_FETCH_DEPTH既定50内容新規クローンのgit fetch深度。fullまたは0で完全取得
CLAUDE_RUNNER_SKIP_GIT_VERIFY既定未設定内容1にするとcheckoutライフサイクルフック実行後の.git存在チェックを省略する

ヘルスエンドポイントとPrometheusメトリクス

RunnerとorchestratorはどちらもGET /healthzとGET /metrics--health-port(既定8080)で提供します。/healthzは「プロセスが生きているか」を返すだけで、200 OKはポールループが正常に回っているかどうかまでは保証しません。Runner側のボディのlast_poll_age_msが増え続けている状態こそが、ループが詰まっているサインです。orchestrator側の/healthzボディは形が異なり、直近ポールが成功したかを示すconnectedと、スポーン要求の状態別件数queue_countsを返します。メトリクス名でも同名のlast_poll_age_secondsはRunnerとorchestratorで意味が違います。Runner側は「直近成功したポールからの経過秒」ですが、orchestrator側は「直近の試行(成功・失敗問わず)からの経過秒」で、--hook-timeoutに由来してだいたい90秒あたりから警戒します。

curl -s localhost:8080/healthz | jq '.last_poll_age_ms'

主要メトリクスを役割別に並べます。

目的Runner側orchestrator側
生存確認Runner側claude_code_self_hosted_runner_last_poll_age_seconds(60秒超で警戒)orchestrator側claude_code_self_hosted_orchestrator_connected(直近ポール失敗で0)
容量Runner側claude_code_self_hosted_runner_capacity / _active_sessionsorchestrator側claude_code_self_hosted_orchestrator_pool_active_sessions(Environment全体、MAXで集計)
待ち行列Runner側orchestrator側claude_code_self_hosted_orchestrator_pool_pending_sessions(queue_pending_sessionsではなくこちら)
失敗の切り分けRunner側claude_code_self_hosted_runner_session_init_errors_total(スポーン前の失敗) / _poll_errors_total{error_kind}orchestrator側claude_code_self_hosted_orchestrator_queue_circuit_broken_sessions(0超で警戒)
バージョン把握Runner側claude_code_self_hosted_runner_info{version}orchestrator側claude_code_self_hosted_orchestrator_info{version}

セッションの生死を数えるカウンターは3種類あります。sessions_completed_totalはクリーンな終了を数えます。sessions_failed_totalは子プロセス自身が非ゼロ終了コードで落ちたケースです。sessions_interrupted_totalは、ドレインや--kill-session-after-minのように、Runner側が運用理由で終了させたケースを数えます。sessions_started_totalから3つの合計を引いた差が、いま動いているセッション子プロセスの数になります。

ここに落とし穴があります。アイドル解放・起動タイムアウト・サーバー側のデアサインは、Runner内部のカウンターでは「クリーンな引き渡し」として扱われ、sessions_completed_totalに計上されます。ところがpost-sessionフックが受け取るCLAUDE_RUNNER_EXIT_REASONでは、同じ3つがどれもinterruptedと報告されます。フックの受信記録をカウンターの値と単純に突き合わせると、完了数を過小に見誤ります。

--capacity 1かつ既定の--drain-grace-sec 0で運用する使い捨てRunner構成では、セッション終了直後にRunner自体が終了するため、通常のスクレイプ間隔(15〜60秒)ではこの3カウンターの増分をほぼ捕捉できません。sessions_started_totalはスポーン時に増えてセッションの生存中は見え続けるので、この構成では「累計」というより「現在稼働中の数」に近い読み方になります。

個々のセッションの詰まりを切り分ける

フリート全体の集計だけでは、特定のセッションが止まっている原因まではわかりません。原因の切り分けは、失敗のタイミングで系列を使い分けます。

スポーン前に落ちたセッションはsessions_failed_totalには現れません。session_init_errors_totalが拾います。checkoutフックの失敗・git準備の失敗・トークンの問題・init前のクラッシュがここに集約されるので、Runnerのイメージやgit認証の設定を疑う起点になります。無事にinitへ到達したセッションの所要時間はsession_init_duration_secondsのヒストグラムで分布を追えます。SessionStartフックがエラーを返した回数はsession_start_hook_errors_totalが別枠で数えます。

個別セッションが止まっている状態そのものを見つけたいなら、session_idle_seconds{session_id,client_platform}が使えます。ターン完了後や操作待ちのままアイドルが続くセッションのゲージで、応答のない権限プロンプトに固まったセッションを見つけて終了させる判断材料になります。

セッション子プロセスのメトリクスも一緒に公開する

各セッションは独自のOpenTelemetryメトリクスを持つ子プロセスとして動きます。--capacityが2以上のとき、2つの環境変数を設定すると子プロセスのメトリクスもまとめて公開できます。Runnerホスト側のOTEL_METRICS_EXPORTER=prometheusと、セッション側のCLAUDE_CODE_ENABLE_TELEMETRY=1です。設定すると、各子プロセスのカウンターとゲージが、Runner自身の系列と並んで/metricsエンドポイントに現れます。ラッパースクリプトやRunner自身の環境変数から渡せば、セッションはそのまま継承します。

Runnerは子プロセスのエクスポーターを内部で書き換え、ヘルスポート上のループバック専用レシーバーへOTLPでpushさせる形にしています。各系列にはsession_idclient_platformラベルが付き、セッションが終わると系列も消えます。ヒストグラムは中継されず、Runner自身のメトリクス名と衝突する子メトリクスは落とされます。既定の--capacity 1ではこの書き換えは働かず、セッションの子プロセスは通常どおり自分でポート9464にPrometheusエンドポイントを持ちます。

Claude Code本体のOpenTelemetry設定(何が送られるか、SIEM連携の要点)はClaude CodeのOpenTelemetryで利用量とコストを可視化にまとめています。セルフホスト環境固有の制御はCLAUDE_CODE_BYOC_ENABLE_DATADOGだけです。既定でオフになっているDatadogのオペレーショナルメトリクス連携を、1を設定することで有効にします。

オートスケーリングの指標選び

キューの深さでスケールするかキャパシティ比率でスケールするかで、見るべき系列が変わります。

スケール方式使う系列
キュー深度型(HPA / KEDA)使う系列claude_code_self_hosted_orchestrator_pool_pending_sessionsqueue_pending_sessionsはスポーン要求単位で、セッション数とは一致しない
キャパシティ比率型使う系列sum(active_sessions)sum(capacity)の比。両方ともRunnerの寿命に関係なく毎スクレイプで値を持つゲージ
ゲート条件使う系列claude_code_self_hosted_orchestrator_connected == 1。切断中のレプリカが古い値をスケーラーに渡さないようにする

全レプリカが切断する完全なポール障害では、ゲート済みクエリは結果を返しません。HPAはメトリクス欠損時にレプリカ数を維持しますが、KEDAのPrometheusスケーラーは既定のignoreNullValues: "true"だと空の結果を0として読み、縮小してしまいます。ignoreNullValues: "false"と、必要ならfallbackのレプリカ下限を設定しておきます。

よくあるつまずき

分単位のフラグとミリ秒の環境変数を混同する: --*-min系フラグは分、対応する環境変数は常に_MSサフィックスのミリ秒です。Helmのvaluesに秒のつもりでミリ秒の値を書くと、既定の15分が15ミリ秒になるような事故が起きます。

poolenvironmentの呼び方の違いに戸惑う: メトリクス名やAPIフィールドは今もpoolという呼び方を使いますが、意味するものはEnvironmentと同じです。環境IDはpool_idフィールドでccpool_...の形式を取ります。

queue_pending_sessionspool_pending_sessionsを取り違える: 前者はorchestratorインスタンス単位のスポーン要求数、後者はEnvironment全体の待機セッション数です。オートスケーラーに渡すべきは後者です。

一発しか処理しないRunnerでカウンターの増分を見ようとする: --capacity 1かつドレイン猶予0の構成では、セッション終了とほぼ同時にRunner自体のメトリクス系列が消えます。スクレイプ間隔によっては増分を一度も観測できません。

--expected-spawn-secondsを実測より短く設定する: 起動が遅いRunnerイメージに対して短すぎる値を設定すると、まだ起動中のセッションが別のRunnerへ再割り当てされ、無駄なスポーンが増えます。

まとめ

Runnerとorchestratorのフラグは、時間単位の癖(分/秒とミリ秒)とセッションカウンターの定義(クリーン/失敗/中断の3分類)を押さえるだけで、大半の設定判断は迷わなくなります。オートスケーリングを組むならpool_pending_sessionsconnectedゲートの組み合わせが起点です。フラグ1つ1つの詳細な意味に迷ったら本記事の表から引きます。固定Runner群ではなくオンデマンド構成にする判断はオンデマンドRunnerの記事を参照してください。

この記事を共有:XはてブLinkedIn