[GitHub Actions] self-hosted runnerにして最初の2週間で起きたこと。ランナー停止2回とアーティファクト128GB

Kenji
Kenji

Engineer / 合同会社irori

公開日
カテゴリ
Web開発
GitHub Actions
Mac mini
CI/CD

こんにちは。

会社のCIをMac miniのself-hosted runnerに移してから最初の2週間で、GitHubホストのランナーのときには起きなかったことが3つありました。どれも大事にはなっていませんが、メモとして残しておきます。

ランナーが止まっていて、26件が18時間並んでいた

9月25日の午後、CIが落ちていたのでClaude Codeに「CI落ちてるの解消して」と頼みました。ドキュメントチェックが1件落ちていたので直したのですが、直した後もステータスがqueuedのまま動きません。30分待っても変わらない。

調べるとこうなっていました。

$ gh api repos/irori-dev/sunao/actions/runs?status=queued --jq '.total_count'
26
$ # 最古の created_at
2026-09-24T12:35:16Z
$ date -u
2026-09-25T06:35:05Z

26件が最長で約18時間キューに並んでいて、in_progressは0。同じ時間に別のブランチで作業していたセッションでも「31件queued、0 running」で、他のIssueブランチも全部滞留していました。動いていたのはClaudeのPRレビューだけで、これはGitHubホストに残していたジョブです。

CIは落ちていたのではなく、ランナーが取りに来ない状態でずっと待っていました。

気づけなかった理由

このときMac mini側の監視はまだありませんでした。GitHubホストだと「キューに並ぶ」という状態がそもそもほぼ無いので、遅ければ何か壊れていると分かります。self-hostedだと、ランナーが死んでいてもGitHub側は静かに待つだけで、PRの画面を見ないと何も起きていないように見えます。

しかも私のアカウントからはgh api orgs/irori-dev/actions/runnersが403で、ランナーの状態が見えませんでした。Organizationの管理者権限が必要です。開発者が「今ランナー動いてる?」を確認できないのは不便でした。

原因

分かっていません。スリープしたのか、ランナーのサービスが落ちたのか、ciのセッションが切れたのか。ログを見る手順も決まっていなかったので、記録が残っていません。

時系列だけ書いておくと、この翌日の午後にmac-mini-infraリポジトリを作り始めていて、そのときの依頼文にはpmset -a sleep 0 ... autorestart 1、ciを自動ログイン、ランナー3台、と書いてあります。監視項目に「各ランナーのRunner.Listenerプロセスが3分以上見つからなければ重大」「ciのGUIセッションが無ければ警告」が入ったのもこの後です。

次にランナーが動かないときの確認順はRUNBOOKに書きました。ciがログインしているか(ps -axo user=,comm= | grep '^ci .*loginwindow')、./svc.sh status、_diag/Runner_*.log、ネットワーク、GitHub側で登録が消えていないか。これで次は原因を残せるはずでした(後述のとおり、残せませんでした)。

Artifact storage quota has been hit

9月27日、CIのDead Code Detectionジョブが、レポートをアップロードするステップで落ちました。

Failed to CreateArtifact: Artifact storage quota has been hit.
Unable to upload any new artifacts. Usage is recalculated every 6-12 hours.

GitHub Actionsのアーティファクト容量が上限に達したというエラーです。集計するとこうでした。

内訳 件数 容量
*-build(8アプリの.nextを固めたtarball) 611 128.2 GB
coverage / knip / sarif など 2,621 0.02 GB
合計 3,232 128.2 GB

99.98%がビルド成果物でした。1回のCIで8アプリ×57〜156MB、合計約0.87GBをアップロードして、retention-days: 7で7日保持。生成ペースは9月26日で44.8GB/日、27日で21.5GB/日。7日保持だと定常で150〜300GBになる計算なので、上限に当たるのは時間の問題でした。

中身を見ると、tarballの59%が.next/cache(265MBのうち156MB)。さらに、このアーティファクトをdownload-artifactしているジョブはゼロ件でした。デプロイはDockerで自前ビルドしています。誰も使っていないものを毎日数十GB積んでいたことになります。

Mac miniのディスクは無事か

self-hostedにした直後だったので、最初に心配したのはMac miniのディスクでした。結論、無関係です。

ジョブはBuild → Compress → actions/upload-artifact → git clean -ffdxq(if: always())の順なので、tarballはアップロード後にMac mini上では消えています。残るのはGitHub側だけ。Mac miniのディスクは使用率39%、空き297GBでした。

つまりこの問題はself-hosted化とは関係なく、GitHubホストのときから進行していたものです。移行のタイミングで各リポジトリのワークフローを全部読んだから見つかった、というほうが正確です。

対応

応急処置として、1日より古い766個を消して128.2GB → 66.4GBまで落としました。候補は「アップロードをやめる」「保持を7日→1日にしてtar --exclude=cache」「developとmainへのpush時だけ上げる」の3つで、同じ日のうちに最初の案に決めてPRを出しています。

  • ci.ymlから成果物の圧縮とアップロードのステップを削除。buildジョブの価値は「ビルドが通ること」で、成果物を残すことではないので
  • 残っているupload-artifact(SARIF、knip、docs QAなど)にはretention-days: 14を明示。未指定だとリポジトリ既定の90日で残る
  • 日次のクリーニングのワークフローを追加。2日より古い*-buildか50MB以上のものだけ消す。使用量の再計算が6〜12時間ごとなので、上限に当たってから消しても最大12時間CIが赤いままになる。だから予防的に回す
  • 回帰テストを追加。.nextのtarballを再びアップロードしていないこと、全部のupload-artifactに30日以内のretention-daysがあることを固定

「消す仕事をMac miniの定期ジョブにする」案も出ましたが、Mac miniにリポジトリへの書き込みトークンを置くことになるのでやめました。sunao側のワークフローで完結させています。

3台同時に、プロセスは生きたまま固まった

10月1日の昼、sunaoのUnit Testsが「実行中」のまま動かなくなりました。timeout-minutes: 25を付けてあるのにタイムアウトも効かず、後ろのBuild 8件が1時間以上queued。

Mac miniはLANから応答するし、./svc.sh statusは3つともStarted、api.github.comにも200が返る。Runner.Listenerは3プロセスとも生きていて、起動から2日半経っています。でもRunner.Workerが1つもいない。ログにはGet messages has been cancelled。GitHub Statusは正常でした。

結局、3つとも./svc.sh stop && ./svc.sh startで戻りました。

for d in ~/actions-runner ~/actions-runner-2 ~/actions-runner-3; do
  (cd "$d" && ./svc.sh stop && ./svc.sh start)
done

固まっていたrunはcancelもforce-cancelもHTTP 409で消せず、そのまま放置しています。根本原因は今回も分かっていません。GitHubとの長時間接続が切れて、Listenerが再接続せずに待ち続けた、くらいの見立てです。

9月25日のときと違うのは、監視が既にあったのに鳴らなかったことです。check.shはRunner.Listenerプロセスの有無しか見ていないので、プロセスが生きたまま固まるケースは拾えません。GitHub側のランナー状態(busy/idle)と突き合わせるには管理者権限のAPIが要るので、まだ手を付けていません。

余談:このとき相談していたClaude Codeは、ランナー3台を「3台の別マシン」だと思っていました。「同じMac miniでプロセスを3つ動かしてる」と伝えてから話が噛み合うようになりました。前提は最初に言うべきですね。

まとめ

  • ランナーが止まってもGitHubは静かに待つ。キューが18時間伸びてもPRを開かないと分からない。監視を作る前だったので原因は不明
  • アーティファクト128GBはself-hosted化とは無関係。ビルド成果物を誰も使っていないのに毎日数十GB積んでいた。アップロードをやめて日次で掃除するようにした
  • プロセスが生きたまま固まるランナーは、プロセス監視では拾えない。svc.shの再起動で戻るが、原因は今回も不明
  • どれも、GitHubホストが隠していた運用を自分で引き受けたということだと思っています

今回はこのあたりで。

この記事を書いた人

Kenji
Kenji(Engineer)

合同会社iroriのエンジニア。Rails / Next.js / AWS を中心とした受託開発と、生成AIを使った開発フローの整備を担当しています。このブログでは、実際に自分の環境で試した手順や、詰まったポイントと解決までの記録を書いています。

Related Posts

[GitHub Actions] 会社のCIをMac miniのself-hosted runnerに移した

[GitHub Actions] 会社のCIをMac miniのself-hosted runnerに移した

[Mac mini] 社内サーバーの構成をリポジトリで管理する。mac-mini-infraを作った

[Mac mini] 社内サーバーの構成をリポジトリで管理する。mac-mini-infraを作った