Claude Codeのhold-idle-fileで残プロセス中のセッションを保持する
セルフホストRunnerのアイドル解放を、ホスト上の残プロセスがある間だけ止める--hold-idle-fileの設定手順。touchの間隔、--max-hold-minの上限、保持が切れる条件をまとめます。
--hold-idle-fileは、セルフホストRunnerがアイドルセッションを解放する時刻を、ホスト側のファイル更新で遅らせるフラグです。セッションがnohupで夜間ビルドなどを走らせたまま黙った場合に、そのプロセスが終わるまでセッションをRunnerに留められます。Claude Code v2.1.311で追加されました。
使い方は短く言えば、Runnerにファイルのパスを渡し、ホストの側から3分より短い間隔でそのファイルをtouchし続けるだけです。ただし、置き場所の権限、--max-hold-minの置き方、止めどきの判定で外すと「保持されない」か「いつまでも解放されない」のどちらかになります。この記事は、その3点を中心に設定から確認まで通します。
--hold-idle-fileは何を遅らせるのか
固定フリートのRunnerは、既定ではターンが終わったセッションもスロットを持ち続けます。これを避けるのが--release-idle-session-minで、指定した分数だけアイドルが続いたセッションを解放します。解放されたセッションは、次のメッセージで再開しますが、pushしていない作業は失われます。
ここで困るのが、セッションがnohupジョブなどをホストに残して黙ったケースです。Claude側は待機中に見えても、ホストでは処理が走っています。--hold-idle-fileは、その間だけ解放を保留する仕組みです。
関係するフラグは3つです。
| フラグ | 環境変数 | 既定値 | 役割 |
|---|---|---|---|
--hold-idle-file <path> | 環境変数SELF_HOSTED_RUNNER_HOLD_IDLE_FILE | 既定値未設定 | 役割ホストが更新し続けるファイル。更新が続く間、アイドルセッションを保持する |
--max-hold-min <n> | 環境変数SELF_HOSTED_RUNNER_MAX_HOLD_MS | 既定値1440(1日) | 役割保持の上限。セッションがアイドルになった時点から数える |
--release-idle-session-min <n> | 環境変数SELF_HOSTED_RUNNER_SESSION_IDLE_MS | 既定値0(無効) | 役割アイドル解放までの分数。保持の対象になる解放はこれ |
3つとも、Claude Code v2.1.311以降が前提です。--release-idle-session-minが0のままだと、そもそも解放が起きないので保持の出番もありません。フラグ全体の一覧はClaude Codeセルフホスト環境のCLIフラグ・メトリクスリファレンスにあります。
設定する4つの手順
前提は、ホスト上で非rootのRunnerを動かしていることです。Runnerがrootで動いていると、セッションがそのファイルを自分でtouchできてしまいます。専用ユーザーでRunnerを動かすところから始めます。
設定から確認までの流れ
- 1
ファイルの置き場所を決める
ローカルの通常ファイルを選びます。例は
/var/lib/claude-hold/hold-idleです。親ディレクトリを含め、セッションが書き込めない場所に置きます。 - 2
Runnerを起動する
--hold-idle-file、--release-idle-session-min、--max-hold-minの3つを渡します。 - 3
残プロセスがある間、touchし続ける
ディレクトリの所有者として、3分より短い間隔で
touchします。 - 4
保持を確かめる
ファイルの更新時刻と、Runnerのログの
idle release heldを見ます。
1. 置き場所はセッションから書けない場所にする
RunnerとセッションはOSユーザーを共有します。同じユーザーが書けるディレクトリにファイルを置くと、セッション自身がtouchして居座れてしまいます。そこで、先に別ユーザー(例はroot)所有、モード755でディレクトリを作ります。
sudo mkdir -p /var/lib/claude-hold
sudo chown root:root /var/lib/claude-hold
sudo chmod 755 /var/lib/claude-holdこの3行はディレクトリの条件を満たす書き方の一例です。親ディレクトリ(/var/lib)もセッションのユーザーが書けないことを確かめてください。
2. Runnerを3つのフラグ付きで起動する
--max-hold-minは保持の上限で、既定は1日です。--release-idle-session-minより大きい値にしないと、何も保持されません。次の例は、30分で解放し、保持中でもアイドルから4時間で解放します。
claude self-hosted-runner \
--environment-secret-file /etc/claude/environment-secret \
--release-idle-session-min 30 \
--hold-idle-file /var/lib/claude-hold/hold-idle \
--max-hold-min 2403. 残プロセスの間だけtouchする
ファイルの最初のtouchが作成を兼ねます。rootのcrontabに次の1行を足す例が示されています。
* * * * * pgrep -x nightly-build > /dev/null && touch /var/lib/claude-hold/hold-idle毎分実行し、nightly-buildという名前のプロセスが動いている間だけtouchします。pgrepの部分は、セッションが残したものをホストが見つける方法に置き換えます。
4. 更新時刻とログで保持を確かめる
プロセスが動いている間は、ファイルの更新時刻が3分以内に保たれているはずです。次のコマンドは、直近3分以内に更新されていればパスを表示し、古ければ何も出しません(手元の確認用の一例です)。
find /var/lib/claude-hold/hold-idle -mmin -3セッションのアイドル時間が尽きたら、Runnerのログにidle release heldの行が出ます。出ない場合は、起動時に出るwatchdog configの行にあるleftover-hold=を読みます。そこに、対象のファイルと上限、または何も保持されない理由が書かれます。
保持が続く時間と終わる時間
保持の長さは、touchの有無と上限の2つで決まります。数字を並べます。
3つの時間の関係
touchの鮮度
3分
最後のtouchから3分で保持が終わる
解放までの余白
約1分
保持が終わってからログに出るまで
上限の既定
1日
--max-hold-minの既定は1440分
最後のtouchから3分たつと保持が終わり、そこからおよそ1分以内にidle release no longer heldがログに出て、Runnerがセッションを解放します。たとえば先の設定で、セッションがアイドルになってから100分後にジョブが終わり、その直前が最後のtouchなら、解放は103分後からおよそ104分後の見当です(公式の秒数からの計算例で、実測ではありません)。
cronを毎分にしているのは、この3分に収めるためです。間隔を5分にすると、touchとtouchの間に保持が切れます。
保持が切れる条件
ファイルを更新し続けていても、保持は終わることがあります。
- 上限に達した:
--max-hold-minが尽きる。長く保持したいなら値を上げる - 強制解放の期限が来た:
--retire-at、--defer-shutdown-max-min、--kill-session-after-minのいずれかを設定していて、その期限が到来する。長く保持したいなら、より遅い期限にする
つまり、保持は解放を遅らせるだけで、期限つきの停止指示には勝てません。--kill-session-after-minはアイドルにならないセッションの安全網でもあるので、保持を使うときは、保持の上限より遅い値にしておくと、保持が先に切れる順序になります。
また、--max-hold-minに--release-idle-session-min以下の値を入れると何も保持されず、0にすると保持機能そのものが止まります。
効いていないときの切り分け
保持されないときの確認順
- QRunnerはv2.1.311以降で、3つのフラグが揃っているか?答えがいいえのとき答えがはい
フラグを足してRunnerを再起動します。
- Q--max-hold-minは--release-idle-session-minより大きいか?答えがいいえのとき答えがはい
上限を引き上げます。以下の値では何も保持されません。
- Qファイルの更新時刻は3分以内に保たれているか?答えがいいえのとき答えがはい
cronの間隔、実行ユーザー、パスの書き込み権限を見ます。
- Qidle release heldがログに出ているか?答えがいいえのとき答えがはい
起動時のwatchdog configにあるleftover-hold=を読みます。
保持の設定は有効です
残る要因は強制解放の期限です。--retire-atなどが近づいていないか見ます。
詰まりやすい2点
落とし穴
残プロセスが終わっても判定が止まらない
touchの条件が、終わったジョブに反応し続けると、アイドルのセッションはすべて上限まで保持されます。pgrepのような判定は、ジョブの終了で確実に不一致になるものにします。
落とし穴
ファイルをセッションが書ける場所に置く
セッションがファイルを更新できると、残プロセスがなくても自分で保持を延ばせます。Runnerをrootで動かしている場合も同じ理由で、専用ユーザーに分けてから使います。
保持を使う場面と使わない場面
--release-idle-session-minを入れたのは、アイドルのセッションがスロットを占めて、ほかのオーナーのセッションがキューに残る状況を避けるためでした。クラウド側のスケジュールタスクが実行ごとに新しいセッションを作る運用では、アイドルのセッションが全スロットを埋めることがあります。解放はその対策です。
保持はその逆向きの例外です。解放の時刻を遅らせるので、スロットの回転は悪くなります。保持を入れる価値があるのは、ホストに残るプロセスの完了を待つ価値が、スロットの占有より大きいときです。たとえば長時間のビルドやデータ処理をnohupで投げたあと、結果をセッションに戻したい場合です。逆に、プロセスが残らないセッションには何も起きないので、設定を入れても害はありません。
保持の有無は、claude_code_self_hosted_runner_session_idle_secondsのメトリクスでも追えます。セッションごとの系列があり、0ならそのセッションは作業中、それ以外はアイドルになってからの秒数です。保持中のセッションがどう見えるかは、手元のRunnerで一度確かめておくと安心です。
上限で解放されたときの作業
保持が上限や強制解放で終わると、セッションは解放されます。解放されたセッションは、ユーザーの次のメッセージで新しいRunnerに再開されますが、そのRunnerは開始ブランチから改めてクローンするため、pushしていない作業は残りません。
コミット済みの作業を守るには、環境内のすべてのRunnerに--push-outcome-on-releaseを設定します。付けたRunnerは、解放の前にoutcomeブランチをベストエフォートでpushし、再開したセッションはそのコミットから始まります。付けていないRunnerに再開されると、開始ブランチからになる点に注意が必要です。有効にする前に、元のリモートでclaude/*のrefにpushできる人を絞っておきます。再開時にRunnerは、以前pushされたブランチを、誰がpushしたかを確認せずに取得するためです。コミットしていない変更は、このフラグでも守られません。
つまり、保持は「待てる時間を延ばす」ものであって、「作業を守る」ものではありません。上限を越えそうなジョブでは、待たせる時間よりも、途中経過をpushする運用のほうが効きます。
まとめ
--hold-idle-fileは、アイドル解放を残プロセスの有無に連動させる一手です。成立する条件は、セッションから書けないパスと、3分より短いtouch間隔、--release-idle-session-minより大きい--max-hold-minの3つです。
アイドル解放そのものの設計はClaude Codeセルフホスト環境の本番運用で、Runner自体の位置づけはself-hosted-runnerの解説で扱っています。保持を入れる前に、まず--release-idle-session-minだけでスロットが空くかを試し、残プロセスで困った時点で足す順序が無理のない進め方です。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。