GitHub Actionsで「失敗したらとりあえず再実行」で回してきたけど、最近ちょっとしんどいなと感じている人に向けて書きました。2026年4月の仕様変更で、再実行には明確な上限が付きました。この記事を読むと、再実行枠をムダにしないCI設計に切り替えられて、障害対応の判断がかなりラクになります。僕は福祉事業のIT全般を担当しつつ、複数案件でAI×SaaSの開発運用も回しているので、現場で使える形に落としてお伝えします。
こんな方におすすめ
- GitHub Actionsで同じ失敗を何度も再実行してしまう人
- CIの失敗が「一時障害か恒久障害か」切り分けられない人
- チームで再実行ルールが曖昧で、運用が属人化している人
- 限られた時間で安定運用まで持っていきたい人
この記事でわかること
- 再実行50回上限を前提にしたCI設計の考え方
- fail-fast・max-parallel・concurrencyの使い分け
- 指数バックオフとrun_attempt監視の実装テンプレート
- 通知と手動介入ポイントを含む運用フロー
2026年4月の変更点|GitHub Actions再実行50回上限で何が変わったか
まず前提です。2026年4月10日にGitHub公式が、1つのworkflow runに対する再実行を最大50回に制限しました。これは全体再実行だけじゃなく、失敗ジョブだけ再実行するケースも含みます。上限を超えるとcheck suiteが失敗扱いになってannotationが返る仕様です。つまり、「気合いで再実行を重ねればいつか通る」は運用として成立しにくくなったということです。
加えて再実行できる期間は初回実行から30日以内です。しかも再実行時の権限は「再実行ボタンを押した人」ではなく、最初にworkflowを起票したactorの権限が使われます。このへんを知らないと、権限が足りなくて復旧が遅れる事故が起きやすいです。公式情報は GitHub Changelog(2026-04-10) と GitHub Docs を見れば追えます。
| 制限項目 | 数値 | 実務インパクト |
|---|---|---|
| Workflow再実行回数 | 最大50回 | 無制限リトライ運用は破綻しやすい |
| 再実行可能期間 | 初回から30日 | 古い失敗は再実行で救済できない |
| Workflow実行上限 | 35日/実行 | 待機や承認詰まりも実行時間に含まれる |
| イベント起因トリガー | 1500 events/10秒/repo | 大量push時に詰まりやすい |
| キュー上限 | 500 runs/10秒 | 突発負荷でrunが取りこぼれる |
| Matrix上限 | 256 jobs/run | 検証軸を増やしすぎると設計見直しが必要 |
上限は「いつか当たる壁」じゃなく、チームが成長するとほぼ確実に当たる壁です。先回りして設計を変えておくと、障害時の心理的負担もかなり軽くなりますよ。
まずは失敗を分類する|再試行すべき失敗・即Failすべき失敗
再実行上限時代の最初の一歩は、失敗を「再試行する価値があるか」で分けることです。ここを分けないまま全失敗を再実行すると、50回の予算をすぐ使い切ります。イメージとしては、CIを1本の自販機に見立てるとわかりやすいです。通信エラーは「お金は入ったけど一時的に詰まった」状態なので再試行が有効ですが、YAML構文ミスは「商品登録そのものが間違っている」ので何回押しても出ません。
- 再試行対象(一時障害) — ネットワークの瞬断、レジストリの一時的な429/5xx、外部APIの短時間障害
- 即Fail対象(恒久障害) — テスト失敗、静的解析エラー、設定ミス、権限不足、シークレット未設定
- 条件付き対象 — flaky test(再現率を計測し、閾値を超えたら修正優先へ切替)
失敗分類の最小ルール
- 同じエラーが2回連続で同一箇所なら、3回目は自動再試行しない
- 構文・型・テスト起因の失敗は即Failして通知に回す
- 再試行対象は「最大回数 + 待機時間」を先に定義してから実装する
この分類だけで、無駄runは体感でもかなり減ります。僕の案件でもこの手順を入れてから、復旧時に「どこまで自動で回すか」の迷いが減りました。まずは直近1週間の失敗ログを見て、一時障害・恒久障害・条件付き失敗の3分類に棚卸しするところから始めるのがおすすめです。
再実行は「失敗したから回す」ではなく、「回す価値がある失敗だけ回す」が基本です。
fail-fast・max-parallel・concurrencyで再実行枠を守る設計
次は、そもそも失敗を増やさない実行設計です。fail-fast はmatrix全体に効くので、主要ジョブが落ちた時点で残りの行列を止められます。continue-on-error を実験ジョブだけに付ければ、守るべき検証は止めずに「遊びの検証」だけ許容できます。ここに max-parallel と concurrency を組み合わせると、同時実行の暴走も防げます。
jobs:
test:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: true
max-parallel: 2
matrix:
version: [6, 7, 8]
experimental: [false]
include:
- version: 9
experimental: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
この設定のポイントは3つです。1つ目は、失敗が確定した時点でそれ以上の消耗を止めること。2つ目は、同じブランチで古いrunを自動キャンセルして「最新コミットだけ検証」へ寄せること。3つ目は、並列数を絞って外部依存の揺れを抑えることです。GitHub公式でも、concurrencyグループは同時にrunning 1 + pending 1へ抑制される仕様が説明されています。
「まず速く失敗させる」設計は、再実行上限対策としてかなり相性がいいです。時間だけじゃなく、運用メンタルの消耗も減らせます。
指数バックオフ実装テンプレート|step内リトライとworkflow再実行の使い分け
再実行を全部workflow単位でやると、上限50回をすぐ削ります。なので一時障害はstep内で吸収するのが基本です。実装はシンプルで、指数バックオフを入れるだけでも効果が出ます。待機を1秒、2秒、4秒、8秒のように伸ばしていく方式です。短い障害をやり過ごせるので、同じ失敗で大きい再実行を引かなくて済みます。
for i in {0..4}; do
if npm ci; then
break
fi
[ ${i} -eq 4 ] && exit 1
sleep $((2 ** ${i})).$((RANDOM % 10))
done
一方で、workflow再実行を使う場面もあります。例えばrunnerの異常や、途中で不安定になった外部サービス依存のケースです。その場合は全再実行よりも失敗ジョブだけ再実行する方が予算効率が高いです。CLIなら gh run rerun RUN_ID --failed、APIなら rerun-failed-jobs が使えます。
- step内リトライ — 一時的ネットワーク障害の吸収
- 失敗ジョブ再実行 — runnerや外部依存の局所障害の回復
- 全体再実行 — 定義変更後の全面再検証が必要なときだけ
さらに2026-04-02の更新でservice containerのentrypoint/command上書きが入りました。起動コマンド差異による不安定をYAML側で吸収しやすくなったので、ここも再試行前に見直すと良いです。
run_attemptと通知設計|再実行予算の可視化、閾値超過アラート、手動介入ポイント
設計ができても、見えなければ運用は崩れます。そこで github.run_attempt を監視軸にします。run_attempt は初回1で、再実行ごとに2,3,4…と増えます。この値で「自動再実行は何回までか」を機械的に止めます。おすすめは、通常運用3回、障害対応時でも5回までです。これを超えたら人が原因を特定するフローに切り替えます。
on:
workflow_run:
workflows: ["CI"]
types: [completed]
jobs:
rerun_failed:
if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.run_attempt < 5 }}
runs-on: ubuntu-latest
steps:
- name: Re-run failed jobs only
run: gh run rerun ${{ github.event.workflow_run.id }} --failed
通知はGitHub標準で、完了時(success/failed/neutral/canceled)または失敗時のみを選べます。なので run_attempt が3以上になったらSlack通知、5でPagerや電話など、段階的に上げると現場が回しやすいです。「再実行できる」より「どこで人が入るか」を先に決めるのがコツです。
通知設計の詳細は 公式の通知ドキュメント も合わせて確認しておくと安心です。運用ルールは、通知先・閾値・手動介入条件をREADMEに残しておくと、担当者が変わっても迷いにくくなります。
コピペで使えるCIテンプレート集(YAML + 運用チェックリスト)
最後に、今日から入れやすい最小テンプレートを置いておきます。ポイントは「守る検証を先に走らせる」「一時障害だけ短く再試行」「古いrunを切る」の3点です。ここまで入れると、再実行50回上限でも運用はかなり安定します。
最小テンプレート(抜粋)
name: ci
on:
pull_request:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-test:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: true
max-parallel: 2
matrix:
node: [20, 22]
experimental: [false]
include:
- node: 23
experimental: true
continue-on-error: ${{ matrix.experimental }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Install with backoff
run: |
for i in {0..3}; do
npm ci && break
[ ${i} -eq 3 ] && exit 1
sleep $((2 ** ${i}))
done
- run: npm test
運用チェックリスト
- 失敗分類 — 一時障害と恒久障害をREADMEに明文化する
- 再実行予算 —
run_attemptの自動停止閾値を決める - 通知段階 — 何回目で誰に知らせるかを固定する
- 重複排除 —
concurrencyとcancel-in-progressを設定する - 棚卸し — 月1で「再実行の多いジョブ」を見直す
テンプレートは完璧さより、まず運用で回ることを優先するのがおすすめです。細かい最適化は、実データを1〜2週間取ってからで十分間に合います。
この設計を後回しにしたときの見えないコスト
再実行50回上限を知らないままだと、障害時に「どこまで自動で回すか」が決まらず、対応速度が落ちる可能性があります。さらに、古いrunが並列で残るとノイズが増えて、原因特定の時間も伸びます。結果として、開発速度が落ちるだけじゃなく、レビュー待ちやリリース待ちが連鎖してチーム全体の生産性が下がりやすいです。小さな設定差が、月単位では大きな時間差になります。
この記事を書いている理由
僕自身、Mac mini M4 Proを24時間稼働させてtmuxで複数セッションを管理しながら、止めない運用をずっと試してきました。結婚式Web招待状サービスで90日以上ログイン継続率を出した時も、裏側は「失敗時にどう復旧するか」の設計が土台でした。業務効率化ツールで月80時間の工数削減を作れたのも同じです。ITを非ITの現場に翻訳する、いわゆる異世界転生の視点で見ると、CIは開発者だけの話じゃなく、事業の体験品質そのものに直結するんですよね。だからこそ今このテーマを届けたいです。
次のアクション
まずは1本だけ主要workflowに concurrency と fail-fast を入れて、1週間の再実行回数を見てみてください。変化が出たら、次にバックオフと通知閾値を追加するとスムーズです。
今日からできるアクション
- 主要workflowに
concurrencyを追加して古いrunを自動停止する - 一時障害のstepに指数バックオフを入れて全体再実行を減らす
run_attemptの閾値(例: 3通知 / 5手動介入)を決める