GitHub

GitHub Actionsのマトリックスビルド|組み合わせ展開とinclude・excludeの調整

GitHub Actionsのマトリックスビルド|組み合わせ展開とinclude・excludeの調整

strategy.matrixは、1つのジョブ定義から複数の組み合わせを自動で作る仕組みです。OSと言語バージョンを並べるだけでテストが横に広がるため導入は簡単ですが、広げた分だけ課金分数と同時実行の枠を消費します。この記事では展開規則と256ジョブの上限、excludeとincludeによる組み合わせ調整、fail-fastとmax-parallelの制御、fromJSONによる動的マトリックス、そして広げてよい条件までを扱いました。ワークフローの基本構造はGitHub Actionsとは?できること・使い方とCI/CD自動化の解説記事、ジョブとテスト実行の組み方はGitHub Actionsでビルド・自動テストを自動化する方法を先に押さえてください。

まとめ:マトリックスビルドで先に決める4つの設計

第一に、マトリックスは変数の直積です。OS3種と言語3種を並べれば9セルになり、そこへ環境2種を足せば18セルへ増えます。セル数は掛け算で伸びるため、変数を1つ足す判断は「テスト時間が何倍になるか」を確認してから下してください。上限は1回のワークフロー実行あたり256ジョブで、この制限はGitHubホストランナーにもセルフホストランナーにも同じく適用されます。

第二に、組み合わせの調整はexcludeとincludeの2つで行い、処理される順序が決まっています。公式ドキュメントは「すべてのincludeの組み合わせはexcludeの後に処理される」と明記しており、この順序のおかげで、いったん広く除外してから必要な1組だけを書き戻す設計が成立します。

第三に、失敗の伝播と並列数は別々のキーで決まります。fail-fastは既定がtrueで、1つのセルが落ちると進行中と待機中の全セルが取り消されます。max-parallelを指定しなければランナーの空き次第で最大限に並びますが、プランごとの同時実行ジョブ数という別の天井が存在しました。

第四に、マトリックスは総実行分数を減らしません。3セルへ分けても消費される分数の合計はほぼ変わらず、短くなるのは体感の待ち時間だけで、費用と速度の交換にあたります。分単価と無料枠の実額はGitHub Actionsの料金|2026年改定後の分単価・無料枠とコスト削減の判断基準で確認できます。

strategy.matrixが変数の直積でジョブを展開する規則

まず展開のされ方を押さえます。ここが曖昧だと、変数を1つ足して所要時間が倍になった理由を説明できません。

変数を並べるだけで全組み合わせのジョブが自動作成される展開規則

strategy.matrixの下に変数名と配列を書くと、その全組み合わせでジョブが走ります。ジョブ定義は1つのままで、実行だけが増える形です。各セルからはmatrixコンテキストで値を参照でき、runs-onにもステップの入力にも差し込めます。

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-24.04, windows-2025]
        node: [20, 22, 24]
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci && npm test

この定義は2×3で6セルへ展開されます。ジョブ名の後ろには組み合わせの値が括弧付きで並ぶため、チェック一覧では6行として表示される構成です。ブランチ保護の必須チェックにジョブ名を登録していると、変数を増やした瞬間に名前が変わって必須チェックが行方不明になります。ランナーのラベルをどう選ぶかはGitHub Actionsのruns-on|ラベル指定の記法と-latest更新・arm64への備えに切り出しました。

生成順は先に書いた変数を外側に置く入れ子構造で決まる仕組みを確認する

公式ドキュメントは、versionに[10, 12, 14]、osに[ubuntu-latest, windows-latest]を与えた6ジョブの生成順を明示しています。version: 10とubuntu、version: 10とwindows、続いてversion: 12の2つ、最後にversion: 14の2つという順序です。先に書いた変数が外側のループになる、と読めます。

順序が効くのは、同時実行の枠が足りずキューが伸びたときでした。枠が2つしかなければ、先頭に並ぶ組み合わせから順に消化されます。落ちやすい構成を早く知りたいなら、その変数を先頭へ書いてください。ただしドキュメントは実行順序そのものを保証しておらず、生成順と完了順は別物として扱う必要があります。

1回の実行で256ジョブという上限はセルフホストにも適用される

1つのマトリックスが生成できるジョブは、1回のワークフロー実行あたり最大256です。ドキュメントはこの制限がGitHubホストとセルフホストの双方へ適用されると書いています。自前のランナーを大量に並べても回避できません。

256は大きく見えますが、変数4本の直積では簡単に届きます。OS4種×言語4種×DB3種×リージョン6種で288セルとなり、この時点で実行は作れません。上限へ近づいたら、変数を減らすか、ジョブを分割するか、includeで代表的な組み合わせだけを列挙する形へ切り替えてください。

excludeで削りincludeで足す組み合わせ調整の4つの型

直積のままで足りる現場はまれです。存在しない組み合わせを削り、特定の1組へ設定を足す作業が要ります。

excludeは部分一致で消えるため記述した数より多く消える条件

excludeの定義は「除外される構成は部分一致でよい」です。全変数を書き切る必要はなく、書いた変数だけが一致すれば、残りの変数がどんな値でもまとめて消えます。

strategy:
  matrix:
    os: [macos-latest, windows-latest]
    version: [12, 14, 16]
    environment: [staging, production]
    exclude:
      - os: macos-latest
        version: 12
        environment: production
      - os: windows-latest
        version: 16

公式例のこの定義は、2×3×2の12構成から3セルが消えて9ジョブになります。1つ目の項目は3変数すべてを指定しているので1セルだけを消しますが、2つ目はenvironmentを書いていないため、stagingとproductionの2セルが同時に消えました。「1行書いたのに2つ減った」という戸惑いの正体はここにあります。

includeは追加と上書きと新規作成という3つの動きを持つ規則

includeは配列ではなくオブジェクトのリストで、1項目ごとに挙動が変わります。公式例はfruitにappleとpear、animalにcatとdogを与えた4セルへ5項目を足し、結果は6ジョブになると説明しています。

include項目 起きること
colorだけ指定 4セル全部に色が付く
colorとanimalを指定 catの2セルだけ色を上書き
fruitとshapeを指定 appleの2セルへ形を追加
未使用のfruit値を指定 新しいセルとして増える
未使用の値と既存値の組 さらに別のセルが増える

読み解き方は単純です。既存の値を壊さずに足せるなら足す、既存の変数と値が一致するセルだけに足すなら上書きを伴って足す、どのセルにも足せないなら新しいセルを作る、という3段階になります。項目は書いた順に適用されるため、先に置いた色より後ろに置いた色が勝ちます。順番を入れ替えると結果が変わる点は、レビューで見落とされやすい箇所です。

excludeの後にincludeが処理される順序で除外を戻す

ドキュメントは「すべてのincludeの組み合わせはexcludeの後に処理される」と明記し、続けて「これにより、以前に除外された組み合わせをincludeで戻せる」と説明しています。処理順が確定しているので、広く消してから一部を戻す書き方が使えます。

実務での型はこうなります。WindowsとmacOSの組み合わせは重いのでexcludeでまとめて落とし、リリース検証に要る「macOS×最新版」の1組だけをincludeで書き戻す。除外条件を細かい論理式で書くより、消してから戻すほうがYAMLは短くなり、意図も読み取りやすくなりました。

代表1組だけに追加設定を与える場合の実務での使い分けと判断基準

もう1つの使いどころが、セルごとの役割分担です。テストは全セルで回すが、カバレッジのアップロードは1セルだけ、という要件はincludeで表現できます。

strategy:
  matrix:
    node: [20, 22, 24]
    include:
      - node: 24
        coverage: true

この形ならnode: 24のセルにだけmatrix.coverageが入り、ifで分岐できます。ジョブを別に切るより設定は減りますが、条件分岐が3つを超えるならジョブ分割へ倒したほうが読みやすい構成です。共通処理そのものを外へ出したい場合はReusable Workflowsとは?GitHub Actionsのworkflow_callで再利用する方法の構成が向いています。

fail-fastとmax-parallelで失敗の伝播と並列数を決める

セルの構成が決まったら、次は落ち方と並び方です。既定のままだと意図しない打ち切りが起きます。

fail-fastは既定trueで1セルの失敗が全セルを巻き込む

fail-fastはマトリックス全体へ効くキーで、既定値はtrueです。ドキュメントは「マトリックス内のいずれかのジョブが失敗すると、進行中と待機中のすべてのジョブを取り消す」と書いています。無駄な分数を使わない挙動ですが、副作用がありました。

Windowsだけで落ちた回に、LinuxとmacOSの結果が取り消されて残らない。原因の切り分けをしたい局面では、この既定が邪魔になります。テストの安定度がまだ低いリポジトリではfail-fast: falseを明示し、全セルの結果を集めてから直す進め方を勧めます。安定してきたら既定へ戻し、分数の節約へ寄せてください。

continue-on-errorはセル単位で失敗の扱いを分けられる

全セルを止めたくないが、特定のセルだけは落ちても構わない。この要件はcontinue-on-errorをmatrixの値で切り替える書き方で満たせます。公式ドキュメントも、実験的なバージョンだけを許容する例を載せていました。

jobs:
  test:
    runs-on: ubuntu-latest
    continue-on-error: ${{ matrix.experimental }}
    strategy:
      fail-fast: true
      matrix:
        version: [20, 22, 24]
        experimental: [false]
        include:
          - version: 25
            experimental: true

この定義では4セルが走り、実験フラグが偽のセルが落ちれば全体が取り消されますが、version: 25のセルが落ちても他は影響を受けません。次期バージョンの先行検証を本流のテストへ混ぜるときの定石です。fail-fastがマトリックス全体、continue-on-errorが単一ジョブという適用範囲の差を押さえておいてください。

max-parallelは既定なしでプラン別の同時実行上限に従う

max-parallelを書かない場合、ドキュメントは「ランナーの空き状況に応じて並列実行数を最大化する」と説明しています。つまり明示的な既定値はなく、ランナー側の都合で決まります。そのランナー側の都合が、プランごとの同時実行ジョブ数でした。

プラン 同時実行ジョブ macOS同時実行
Free 20 5
Pro 40 5
Team 60 5
Enterprise 500 50

macOSの枠が特に狭く、どのプランでも同時5本という制限があります。macOSを含む大きなマトリックスを組むと、他のワークフローまで巻き添えで待たされます。max-parallelを明示する動機はここにあり、共有リポジトリで枠を独占しない上限として使うのが実用的です。セル内の並列数ではなく実行そのものを1本へ絞りたい場合は、GitHub Actionsのconcurrency|groupの設計とcancel-in-progress・queueの選び方で扱うconcurrencyが別の打ち手になります。外部APIへ同時接続する結合テストのように、並列度を絞りたい場合にも同じキーを使います。

fromJSONで実行時にマトリックスを組み立てる動的な構成

YAMLへ値を直書きすると、対象が増えるたびに編集が発生します。前段のジョブで対象を計算し、後段のマトリックスへ流し込む構成にすれば、その手間は消えます。

前段ジョブのoutputsへJSON配列を出しfromJSONで受ける

公式ドキュメントが示す型は、1つ目のジョブでJSON配列をジョブ出力へ書き、2つ目のジョブがfromJSONで受け取る形です。needsで依存関係を張るのが前提になります。

jobs:
  define-matrix:
    runs-on: ubuntu-latest
    outputs:
      targets: ${{ steps.set.outputs.targets }}
    steps:
      - id: set
        run: echo 'targets=["api","web","batch"]' >> "$GITHUB_OUTPUT"
  build:
    needs: define-matrix
    runs-on: ubuntu-latest
    strategy:
      matrix:
        target: ${{ fromJSON(needs.define-matrix.outputs.targets) }}
    steps:
      - run: make build TARGET=${{ matrix.target }}

fromJSONが受け取れるのは文字列のJSONです。配列の要素をオブジェクトにすれば、1セルへ複数の値を渡せます。前段ジョブが1本増える分だけランナーの起動時間が上乗せされる点は勘定に入れてください。ワークフロー読み込み段階で確定する値でよいなら、リポジトリ変数から組み立てる方法もあります。変数の置き場所と参照可能な位置はGitHub Actionsの環境変数|env・vars・secretsの使い分けと受け渡しで整理しました。

変更のあったディレクトリだけを対象に回すモノレポでの実装手順

動的マトリックスが最も効くのはモノレポです。前段ジョブで変更ファイルの一覧を取り、影響のあるパッケージ名だけをJSON配列へ詰めます。全パッケージを毎回回す構成と比べ、日常のプルリクエストで動くセル数が減りました。

差分の取得にはgit diffの結果を加工する方法と、パスフィルタのアクションを挟む方法があります。どちらでも構いませんが、ベースブランチとの比較が要るため、チェックアウトの深さには注意してください。浅いクローンのままだと比較対象のコミットが手元に無く、差分が空になります。

配列が空になるとジョブごと消えるため後段へ実行条件を足す安全策

差分ベースの動的マトリックスには落とし穴があります。変更が1つも無ければ配列は空になり、後段のジョブはセルが0個になって実行されません。スキップではなく生成されない扱いでした。

ブランチ保護の必須チェックへそのジョブ名を登録していると、チェックが永久に完了せずマージできない状態になります。回避策は2つです。空配列のときはダミーの1要素を入れて必ず1セル走らせるか、必須チェックを集約ジョブへ移し、そのジョブを常時実行の条件付きで走らせて結果を判定する。後者のほうが構成は複雑ですが、セル数の増減に強くなります。

並列を広げるほど課金分数が増える釣り合いをCI運用で判断する

ここが判断の芯です。広げる相談を受けたとき、私たちは速度ではなく分数の総量から確認します。

マトリックスは総実行分数を減らさず待ち時間だけを短縮する費用構造

6分のテストを3セルへ分ければ、待ち時間は2分前後まで縮みます。ところが消費される分数は3セル合計でおよそ6分のままで、セットアップと依存解決が各セルで重複する分、むしろ増えました。並列化は分数を時間へ交換する取引であって、節約ではありません。

この前提が共有されていないと、「テストを速くする」目的でセルを増やし、翌月の請求で驚く展開になります。1回あたりの分数、1日の実行回数、月間の営業日を掛けた総量を先に出してください。GitHubホストランナーの実行時間上限は1ジョブ6時間、セルフホストは5日で、待機は24時間で自動的に取り消されます。

プルリクは代表1組でマージ時に全面実行する二段構えにする運用

私たちが受託案件で採るのは、実行契機でセル数を変える構成です。プルリクエストでは主要な1組だけを回して数分で返し、既定ブランチへのマージとナイトリーでは全セルを回して互換性を確認します。同じワークフローファイルで書き分けるなら、includeで代表セルを定義し、イベント名を条件にして除外側を切り替える形が扱いやすくなりました。

この構成にすると、開発者が待つのは常に短いほうのマトリックスになります。互換性の網羅は1日1回で足りる案件がほとんどで、プルリクエストのたびに18セルを回す必要はまずありません。実行契機そのものの設計はトリガーの選び方に依存するため、パイプライン全体の考え方はCI/CDとは?仕組み・パイプライン・導入すべき企業の判断基準を解説と併せて検討してください。

マトリックスを広げてよい条件と広げてはいけない判断基準を整理する

採用の条件は3つあります。第一に、そのセルが落ちたときに実際に修正する意思があること。誰も直さないOSを回すのは分数の浪費です。第二に、セル間でテスト内容が本当に異なること。同じコードパスを別のNode.jsバージョンで通すだけなら、1バージョンで足ります。第三に、1セルあたりの実行時間が短いこと。20分のセルを10並列にすると、失敗のたびに200分が飛びます。

逆に見送るべき場面も明確でした。テストが不安定で再実行が常態化しているなら、セルを増やすほど失敗確率は掛け算で悪化します。セットアップに3分かかりテスト本体が30秒の構成も、分割の効果より重複コストが上回るため向きません。ライブラリの公開のように対象環境が仕様で決まっている場合を除き、まずは1セルで回して所要時間を測るところから始めてください。CI基盤の棚卸しや、組んだパイプラインの引き取りが必要なら保守運用・内製化支援でご相談いただけます。

マトリックス特有の3つの事故を設定で防ぐ切り分けと確認手順まで

最後に、マトリックスでのみ起きる故障の型を挙げます。単一ジョブでは出ないため、原因にたどり着くまで時間を取られます。

全セルが同じ名前でoutputsを書くと最後の1件しか残らない

マトリックスジョブのoutputsは、ドキュメントによれば「マトリックス内の全ジョブから結合される」仕様です。ただし同じ名前で書くと結合できません。警告文は「Actionsはマトリックスジョブの実行順序を保証しない。出力名は一意にすること。さもなければ最後に走ったジョブが値を上書きする」と述べています。

各セルの結果を後段へ渡したいなら、出力名へセルの識別子を含めてください。それでも後段はtoJSONで全体を受け取って自分で解釈する必要があります。セルごとの成果物を確実に持ち越すなら、アーティファクトへ書き出して名前で取り分けるほうが堅く組めます。セルごとの命名と受け取り側での集約は、GitHub Actionsのアーティファクト|受け渡しの設計と同名不可・保持期間の決め方で扱っています。

文字列結合でラベルを組み立てるとキュー待ちのまま止まる原因と対処

runs-onへOS名とアーキテクチャを結合した文字列を書く構成は、片方の値を変えた瞬間に存在しないラベルを生みます。存在しないラベルのジョブは構文検証を通過し、待機したまま進みません。24時間経つと自動的に取り消されるだけで、失敗通知は出ないため気付くのが遅れました。

回避策は、ラベルを結合で作らずincludeで明示することです。OS名とランナーのラベルを対にして書いておけば、組み合わせと実ラベルが1対1で対応します。ラベルの正しい綴りと世代交代の見通しはruns-onのラベル指定と-latest更新の解説記事を参照してください。

セル数だけキャッシュ保存が走りレート制限に触れて落ちる原因と対策

依存キャッシュを各セルで保存する構成は、セル数が増えるほど同時保存が集中します。キャッシュサービスにはリポジトリ単位で毎分200アップロードという制限があり、大きなマトリックスでは一部の保存が静かに失敗しました。ログを追わない限り、効かないまま分数だけ増えます。

設計としては、依存の取得と保存を代表1ジョブへ寄せ、残りのセルは復元専用にするのが定石です。key設計と復元されないときの切り分けはGitHub Actionsのキャッシュ|actions/cacheのkey設計と復元されない原因の切り分けにまとめました。マトリックスを広げる前に、キャッシュ側の構成を先に見直すほうが効く場面は多いはずです。

よくある質問

マトリックスのセル数に上限はありますか?

あります。1回のワークフロー実行あたり256ジョブが上限で、GitHubホストランナーとセルフホストランナーの双方へ適用されます。変数を4本並べると意外と早く到達するため、上限が近いならジョブ分割かincludeによる代表セルの列挙へ切り替えてください。

fail-fastの既定値はtrueとfalseのどちらですか?

既定はtrueです。いずれかのセルが失敗すると、進行中と待機中のセルがすべて取り消されます。全セルの結果を集めてから直したい場合はfail-fast: falseを明示してください。特定のセルだけ許容したいならcontinue-on-errorを使い分けます。

excludeとincludeはどちらが先に処理されますか?

excludeが先で、includeが後です。ドキュメントはこの順序を明記したうえで、除外済みの組み合わせをincludeで書き戻せると説明しています。広く消してから必要な1組だけを戻す書き方が可能になるのは、この処理順のためでした。

max-parallelを指定しないと何本まで同時に走りますか?

ランナーの空き状況に応じて最大限並びます。上限はプランごとの同時実行ジョブ数で、Freeは20、Proは40、Teamは60、Enterpriseは500です。macOSはどのプランでも同時5本、Enterpriseでも50本に制限されるため、macOSを含むマトリックスでは特に枠を意識してください。

マトリックスにすればCIの費用は下がりますか?

下がりません。消費される分数の合計は分割前とほぼ同じで、セットアップの重複がある分だけ増えます。短くなるのは開発者の待ち時間です。費用を下げたいなら、セルの刈り込み、キャッシュの見直し、ランナー種別の変更という別の打ち手を先に検討してください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2026.10.08 テックブログ 大阪公立大学のランサムウェア被害と仮想化基盤の停止|全授業休講に至った経緯とバックアップを守る設定
  3. 2024.11.08 テックブログ OpenAPI GeneratorでJavaコードを自動生成する方法|CLI導入からSpring・ライブラリ選択まで
  4. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順
  5. 2026.10.09 テックブログ 京王電鉄のランサムウェア被害とグループ共通基盤:決済・ポイント・予約が止まった範囲と遮断の初動

RELATED POSTS 関連記事

目次