Claude Codeをcronで無人稼働させ始めてから半年ほど経ちますが、最初の1ヶ月で肝を冷やしたのは「動かす」設計ではなく「止める」設計の不足でした。動かす方法はネットにいくらでも情報がありますが、暴走した時にどう止めるか、多重起動をどう防ぐかを先に決めているケースは意外と少ない印象です。
この記事は、Claude CodeをLinuxのcronに載せて無人運用する際に私が実際に組み込んだ安全装置——kill switch・PIDロック・ログ管理——だけに絞った設計ノートです。動かし方の網羅ガイドではなく、「止める」「多重起動させない」「肥大化させない」という守りの部分に特化しています。
想定した3つの事故パターン
無人cronを組む前に、まず起こりうる事故を洗い出しました。
| 事故パターン | 何が起きるか | 気づくタイミング |
|---|---|---|
| 多重起動 | 前回の処理が終わる前に次のcronが起動し、同じファイルへの書き込みが競合する | ログが壊れて初めて気づく |
| 暴走の継続 | 想定外の入力でループ的に処理が回り続け、API課金が積み上がる | 請求書を見て気づく |
| ログの肥大化 | 標準出力を>>で溜め続け、ディスクを圧迫する |
ディスクフルでcron自体が死んで気づく |
この3つに対して、それぞれ「止める仕組み」を先に用意してから本番投入する、という順番にしました。
Kill Switch — 止める権限を1つのファイルに集約する
一番怖いのは「異常に気づいたが、止め方が分からない」状態です。cronのコメントアウトはSSH接続が必要ですし、深夜だと対応が遅れます。そこで私は、特定のマーカーファイルが存在する間は全ジョブを実行しないという単純な仕組みを入れました。
#!/usr/bin/env bash
# run-claude-job.sh
set -euo pipefail
KILL_SWITCH="/home/user/ops/PAUSED"
if [ -f "$KILL_SWITCH" ]; then
echo "$(date -Iseconds) kill switch有効のためスキップ" >> /home/user/ops/logs/skip.log
exit 0
fi
claude -p "$1" --allowedTools "Read,Grep" --max-turns 15
touch /home/user/ops/PAUSED を打つだけで全ジョブが止まり、rmで再開する。SSHさえ繋がればコマンド1つで止められるので、深夜にスマホから対応するときも迷いません。
このマーカーファイル自体の権限設計でも一度ハマっています。以前、Webサーバーの公開ディレクトリに.envが置かれたままになっていて、URLから直接内容が見えてしまう脆弱性を見つけたことがありました。原因はドキュメントルートの絞り込み漏れで、設定ファイルの類は「デフォルトで見せない」を徹底していなかったことでした。この経験があったので、kill switch用のディレクトリはchmod 700でオーナー以外アクセス不可にし、Webから到達できない/home/user/ops/配下に置くようにしています。制御用ファイルこそ、うっかり公開されると洒落にならないという教訓です。
PIDロック — 多重起動を機械的に防ぐ
cronの実行間隔より処理が長引くと、前回の実行が終わらないうちに次が始まってしまいます。人間が時間を計算して間隔を空ける運用は、負荷が変動すると必ず破綻するので、flockでロックファイルを取る方式にしました。
#!/usr/bin/env bash
# run-claude-job.sh(PIDロック追加版)
set -euo pipefail
LOCK_FILE="/tmp/claude-job.lock"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
echo "$(date -Iseconds) 前回の実行が継続中のためスキップ" >> /home/user/ops/logs/skip.log
exit 0
fi
claude -p "$1" --allowedTools "Read,Grep" --max-turns 15
# flockはスクリプト終了時にfd 9のクローズで自動解放される
flock -n(ノンブロッキング)にしているのがポイントで、ロックが取れなければ待たずに即スキップします。cron側で待たせてしまうと、待機中のプロセスが積み上がって余計に事故が起きやすくなるので、「今回は諦めて次回に回す」設計にしました。
Linuxのファイアウォール設計で「必要なポートだけ開けて、それ以外はデフォルト拒否」という原則を徹底したことがありますが、kill switchとPIDロックもまったく同じ発想です。平常時は全ジョブが動く「許可」状態、異常時だけ明示的に「拒否」に倒す——この非対称性を先に決めておくと、止め忘れというヒューマンエラーが構造的に起きなくなります。
ログ管理 — 標準出力を溜め続けない
>> log.log 2>&1 で溜め続けると、数ヶ月単位で確実にディスクを圧迫します。以前、アプリケーションログの肥大化でディスク使用率が95%を超えてアラートが鳴り止まなくなったことがあり、その時に整えたlogrotateの設定をClaude Codeのジョブログにもそのまま流用しました。
# /etc/logrotate.d/claude-jobs
/home/user/ops/logs/*.log {
daily
rotate 7
compress
missingok
notifempty
copytruncate
}
copytruncateにしているのは、cronスクリプト側でファイルディスクリプタを掴んだまま>>し続けても安全にローテーションできるようにするためです。この設定にしてから、私の環境ではディスク使用率が40%前後で落ち着き、7日分のログをgzipで保持しつつそれ以前は自動で消える状態を維持できています。
加えて、Claude Codeのログには実行結果だけでなく--output-format jsonで拾えるトークン使用量やAPIキーの断片が混ざり込むことがあるので、rotate前にgrep -vでマスキングするフィルタを1行挟んでいます。制御用の設定ファイルと同じで、ログも「うっかり見えてはいけないもの」が混ざる前提で扱うようにしています。
定期的な安全性チェックも自動化する
kill switchやPIDロックは「仕込んだら終わり」ではなく、定期的に機能しているか確認する必要があります。以前、OSやミドルウェアのセキュリティパッチ適用が属人的になっていて、半年以上パッチが当たっていないサーバーが見つかったことがありました。「仕込んだ対策は放置すると必ず陳腐化する」という学びがあったので、Claude Codeの安全装置についても、週次で以下を確認するチェックジョブを別途cronに登録しています。
#!/usr/bin/env bash
# weekly-safety-check.sh
set -euo pipefail
# kill switchディレクトリの権限確認
PERM=$(stat -c "%a" /home/user/ops)
[ "$PERM" = "700" ] || echo "警告: kill switchディレクトリの権限が変わっています($PERM)" | notify
# ロックファイルが異常に古い(前回実行が詰まっている)場合を検知
if [ -f /tmp/claude-job.lock ]; then
AGE=$(( $(date +%s) - $(stat -c %Y /tmp/claude-job.lock) ))
[ "$AGE" -gt 3600 ] && echo "警告: ロックファイルが1時間以上残留中" | notify
fi
この週次チェックを入れてから、kill switchのディレクトリ権限がデプロイ作業で意図せず戻ってしまう、といった「安全装置自体の劣化」に気づけるようになりました。
安全設計チェックリスト
無人cronに載せる前の確認事項をまとめておきます。
- マーカーファイルの有無で全ジョブを止められるkill switchがある
-
kill switch用ディレクトリは
chmod 700など最小権限にしてある -
flock等で多重起動を機械的に防止している - ロックはノンブロッキングで取り、失敗時は待たずにスキップする
-
ログは
logrotate等で世代・サイズを上限管理している - ログに紛れる可能性のある機密情報をマスキングしている
- kill switch・ロックの権限や状態を定期チェックするジョブを別途持っている
まとめ
- 多重起動 → PIDロック
- 暴走・止め忘れ → kill switch
- ログ肥大化 → logrotate
- 安全装置自体の劣化 → 定期チェック
無人運用の記事は「動かす」方法に注目が集まりがちですが、私の感覚では**「止める」設計を先に固めてからでないと、本番の頻度を上げる決断ができません**。特にkill switchは、実装コストの割に「深夜に安心して寝られるかどうか」への効き目が一番大きい投資だったと感じています。
今回はLinux単体での安全設計に絞りましたが、複数サーバー・複数ジョブを横断した監査ログの集約は別記事で扱う予定です。Claude Codeや業務自動化まわりの実装で試したことを不定期に書いているので、続きが気になる方は フォロー をどうぞ。