これまで GitHub Actions の並列化といえば、needs や matrix による job 単位の話でした。同一 job 内の steps は上から順にしか動きません。非同期にしたいときは、シェルの & でバックグラウンド実行する程度の回避策しかありませんでした。& はログが混ざりやすく、ステップ単位の成否や出力の扱いも弱いです。
2026 年 6 月下旬から、同一 job 内で steps を並列実行できます。キーワードは background、wait / wait-all、cancel、そして糖衣の parallel です。ログはステップごとに分かれます。長時間サービスを起動してから本処理を走らせ、終わったら止める、といったパターンも YAML 上で明示できます。
構文
parallel
独立したステップ群をまとめて同時実行し、すべて終わるまで次へ進まない書き方です。各ステップは内部的に background になり、グループ末尾で暗黙の wait が走ります。
steps:
- uses: actions/checkout@v6
- parallel:
- name: Build frontend
run: npm run build:frontend
- name: Build backend
run: npm run build:backend
- name: Build docs
run: npm run build:docs
- name: Run tests after all builds complete
run: npm test「この塊だけ同時に走らせ、全部終わったら次へ」というパターンには、parallel が簡潔です。
background / wait / wait-all / cancel
細かい制御が必要なら background: true を使います。起動したステップはすぐ次へ進み、必要なタイミングで待つ・止めます。
steps:
- name: Build frontend
id: build-frontend
run: npm run build:frontend
background: true
- name: Build backend
id: build-backend
run: npm run build:backend
background: true
- name: Run linter while builds run
run: npm run lint
- name: Wait for both builds to finish
wait: [build-frontend, build-backend]
- name: Run tests
run: npm testwait: 文字列、または配列で指定したidの background ステップ完了を待つwait-all: 実行中の background ステップすべてを待つ。引数はありませんcancel: 指定idの background ステップへ終了信号を送る。まずSIGTERM、終了しなければSIGKILL
parallel は便利ですが、表現力は限られます。サーバを立てたまま後続ステップを動かし、最後に止める流れは background 向きです。
公式上の制約
次を押さえておくと事故が減ります。
- 同一 job で同時に走れる background ステップは最大 10。超えた分はスロットが空くまでキューに入ります。foreground のステップは、background のスロットが埋まっていてもすぐ実行されます
- composite action の中では
background/parallelを宣言できません。composite 自体を background ステップとして走らせることはできます wait/wait-all/cancelにifは付けられません。常に実行されます- background ステップの outputs や環境変更は、それを含む
wait/wait-allの後でのみ後続から使えます - background が失敗すると、それを待つ次の
wait/wait-allで job が失敗します。当該ステップにcontinue-on-errorがある場合は除きます - ジョブ終了前の post 処理の前に、未 wait の background に対する暗黙の
wait-allが走ります
job 並列との使い分け
| 観点 | job 並列(needs / matrix) | steps 並列(parallel / background) |
|---|---|---|
| ランナー | 別ランナー | 同一ランナー |
| セットアップ | 各 job で重複しやすい | 1 回の setup を共有できる |
| ファイルシステム | 分離 | 共有 |
| 向いてる処理 | CPU / メモリを食い合う重い独立処理 | I/O 待ちの重ね合わせ、軽い独立チェック、サービス起動 |
| 具体例 | Go / Rust のクロスコンパイル、重い production build、CPU を張り付くテスト一式、複数 OS / Node 版の matrix | 独立したデプロイの安定待ち、lint / format / 軽い typecheck、ビルド中の lint、dev server 起動後のテスト |
CPU をほぼ使い切る独立タスクは、従来どおり matrix や別 job の方が終わるまでの時間が短くなりがちです。待ち時間が支配的な処理や、同一ワークスペース上の軽いチェックは steps 並列の方が向きます。
表の job 並列には、別 workflow に分けて同じイベントで並走させる場合も含めて考えてよいです。ランナーが分かれ、setup が重複し、所要時間とコストのバランスも別 job と同型だからです。steps 並列へ寄せる価値が出るのは、トリガーが同じで権限を共有してよく、軽い検査同士を 1 回の setup 後に畳みたいときです。待ちの多い処理と軽い検査の組み合わせでも同様です。主に効くのはコストです。所要時間は、別 workflow のまま並走していた場合と大きく変わらないことが多いです。次のようなときは分けたままの方がよいです。
- トリガーが違う。たとえば PR では lint のみ、本番ブランチだけデプロイする、など
- デプロイ用の強い secrets や environment を lint 側に載せたくない
- 必須チェックや失敗の意味を workflow 単位で分けたい
最適化パターン
効果の見方は 2 つあります。ワークフローや job が終わるまでの所要時間と、動いた runner の合計時間です。後者は課金の目安になるコストです。同じ parallel でも、パターンによって前者を縮めるもの、後者を抑えるもの、どちらもあまり期待できないものがあります。
A. 独立ビルドを並べる
互いに依存しないビルドを直列に並べているなら、parallel で最長の 1 本に近づけます。狙いは所要時間の短縮です。同一ランナー上なので、job が短くなればその job 分のコストも連動して下がることがあります。
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci
- parallel:
- name: Build frontend
run: npm run build:frontend
- name: Build backend
run: npm run build:backend
- name: Build docs
run: npm run build:docsB. I/O・待ち時間の重ね合わせ
デプロイ先の安定待ちのように、ランナー上ではほとんど CPU を使わず外部完了を待つステップは、互いに独立なら並列化の効果が大きいです。直列だと待ち時間が足し算になり、並列だと長い方に律速されます。ここでも主に縮むのは所要時間です。待ちの足し算が消えるぶん、その job のコストも下がりやすいです。
steps:
# task definition の準備などは省略
- parallel:
- name: Deploy web service
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.render-web.outputs.task-definition }}
wait-for-service-stability: true
- name: Deploy worker service
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.render-worker.outputs.task-definition }}
wait-for-service-stability: trueたとえば基盤のあとアプリ、といった依存があるデプロイは並列にしません。
C. サービス起動・本処理・停止
アプリ本体の dev server やローカルプロセスを立ててからテストし、終わったら止めます。主眼は利便と制御です。ログがステップごとに分かれ、成否や停止も YAML 上で明示できます。所要時間やコストを大きく削る手法というより、シェルの & や無理な job 分割をやめるための書き方です。もともとサーバ起動用に別 job を立てていたなら、1 job に寄せることでコストは下がることがあります。
steps:
- uses: actions/checkout@v6
- run: npm ci
- name: Start server
id: server
run: npm start
background: true
- name: Run tests against the server
run: npm test
- name: Stop the server
cancel: servercancel に if は付けられませんが、制御フロー用ステップとして常に実行されます。テスト失敗時もサーバ停止の意図は残ります。コンテナ化された依存だけなら jobs.<job_id>.services も選択肢です。リポジトリ内の npm start のような通常ステップには background が合います。
D. ビルドと Lint のインターリーブ
ビルドを background で走らせている間に Lint など別系統の作業を進め、必要な成果物の完了だけ待ちます。ビルド待ちの隙間を埋めるので、所要時間が縮みやすいです。job が短くなれば、そのぶんコストも連動して下がることがあります。
steps:
- uses: actions/checkout@v6
- run: npm ci
- name: Build frontend
id: build-frontend
run: npm run build:frontend
background: true
- name: Build backend
id: build-backend
run: npm run build:backend
background: true
- name: Lint while builds run
run: npm run lint
- wait: [build-frontend, build-backend]
- name: Integration tests
run: npm testE. 非ブロッキングな副作業
本処理と独立したアップロードやテレメトリ送信を background で始め、パッケージングなどを続け、最後に揃えます。本処理と副作業の待ちを重ねるので、所要時間の短縮が主目的です。job が短くなればコストも連動して下がることがあります。
steps:
- name: Upload telemetry
id: telemetry
run: ./scripts/upload-telemetry.sh
background: true
- name: Package release
run: ./scripts/package.sh
- wait: telemetry副作業の失敗で job を落としたくない場合は、background 側に continue-on-error: true を検討します。
F. 軽いチェックの畳み込み
もともと別 job で並走していた lint / format / typecheck を、1 job の setup 後に parallel でまとめます。別ランナーでの並走です。終わるまでの所要時間は、もともと job 並列だった場合と大きく変わらないことが多いです。ここで効くのはコスト側です。checkout や install の重複が減り、runner の合計時間が下がります。
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci
- parallel:
- name: Lint
run: npm run lint
- name: Format check
run: npm run format:check
- name: Type check
run: npm run typecheck
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci
- run: npm test重い test やメモリを食う build まで同じランナーに詰め込むと、次のアンチパターンになりがちです。軽い独立チェックだけ畳み、重い処理は別 job に残す併用が現実的です。
G. アンチパターン: 低スペックへの詰め込み
ubuntu-slim は 1 CPU / メモリ 5 GB の軽量ランナーです。ここに CPU やメモリを食う複数ステップを parallel で載せると、互いに奪い合って各ステップが単独実行より遅くなり、job 全体が最長ステップに引きずられます。所要時間もコストも改善せず、悪化することがあります。
H. アンチパターン: CPU ヘビーな steps 並列
Go のクロスコンパイルのように、各ステップが CPU をほぼ使い切る独立ビルドを同一ランナーで parallel しても、コア数が足りなければコンテキストスイッチが増えるだけで所要時間はほとんど縮まないことがあります。steps 並列だけでは、所要時間もコストもあまり期待できません。終わるまでの時間を優先するなら、strategy.matrix や別 job でランナーを分ける方が速くなりがちです。そのぶん runner 合計時間と課金は増えやすいです。
まとめ
steps 並列で狙える効果は大きく次の 2 つです。
- 直列だった独立作業を並べ、終わるまでの所要時間を最長のものに近づける。待ち時間の重ね合わせが特に効きます
- 別ランナーに分散していた軽いチェックを 1 job に畳み、セットアップ重複を減らしてコストを抑える
どちらも、同一ランナーの CPU とメモリを共有する制約を抱えます。重い処理は job 並列、別 workflow も含めてそちら側です。軽い独立作業と I/O 待ちは steps 並列です。適切に使い分けましょう。