GitHub Actions の同時実行制御を図解する — concurrency group と queue: max

numa

はじめに

こんにちは、ぬまです。

2026年5月、GitHub Actions の concurrency に queue: max が追加されました(GitHub Changelog)。これは、最大 100 件まで滞留できる機能でして、pending が 1 件しか置けなかった問題を緩和できるものです。

AI による開発が加速化する昨今、GitHub Actions の同時実行数を考慮した設計も増えてくるかと思います。

この記事では、新しくリリースされた機能を含め、GitHub Actions の同時実行制御について図解していきます。

なぜ 同時実行数(concurrency)の設計が大事なのか

GitHub Actions は、特に指定しなければ、同じ workflow の複数 run が同時に走ります。CI のテストや lint なら並列でも問題になりにくい一方、デプロイのように 同じ環境へ書き込む処理 では同時実行を考慮した設計を求められることがあります。

concurrency を付けない場合、同じ環境向けのデプロイが並列に走り得ます。

この並列は、次のようなリスクにつながります。

  • 後から始まったデプロイが、先に走っていたデプロイの結果を上書きする
  • 途中状態のまま環境が参照され、一時的に不整合になる
  • runner や外部 API の利用枠を想定以上に消費する

AIコーディングエージェントの活用などにより、短い間隔でcommitやPRが作成される開発スタイルも増えています。そのような場合、GitHub Actionsの起動頻度が高まり、同じ環境に対するデプロイや検証が重なりやすくなります。

concurrency は、同じ concurrency group を使う run のうち、同時に実行できる本数を制御する仕組みです。まずどの concurrency group にまとめるかを決め、そのうえで待ちの深さやキャンセル可否を設計します。

concurrency group

concurrency の group は、workflow や job を concurrency group にまとめるためのものです。公式ドキュメントでは、同じ concurrency group を使うジョブまたはワークフローを一度に 1 つだけ実行する、と説明されています。

  • 同じ group なら、同じ concurrency group に属し、同時実行が制限される
  • 違う group なら、別の concurrency group になり、互いに干渉しない

cancel-in-progress も queue も、同じ concurrency group の内側にだけ効きます。group の決め方を間違えると、後段の設定がどれだけ正しくても意図どおり動きません。

デプロイでよく使う group の決め方は、次の 2 つです。

# 環境単位: 本番へのデプロイを 1 つの concurrency group にまとめる
concurrency:
  group: production-deploy

# ブランチ単位: ブランチごとに別の concurrency group にする
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}

ここまでが concurrency group についての説明でした。以降は、同じ group の中で実行中を止めるか、待ちをどう扱うかを見ていきます。

cancel-in-progress とデプロイ

同じ group の中では、実行中の run を止めるかも選べます。

設定挙動デプロイでの向き不向き
cancel-in-progress: true同じ group の実行中 run もキャンセルし、新しい run を優先プレビュー向け。本番の途中停止には注意
false または未指定実行中は完走させ、新しい run は pending 側で扱う本番デプロイ向き

本番デプロイでは、実行中の適用を途中で止めると環境が中途半端な状態になり得ます。そのため、本番環境では cancel-in-progress を付けない(または false)ことが多いと思われます。

特定ブランチだけ進行中ジョブをキャンセルする

cancel-in-progress には真偽値だけでなく、式を渡せます。公式ドキュメントでは、開発ブランチではキャンセルし、release ブランチではキャンセルしない、といった例が紹介されています。

デプロイ文脈では、次のように使い分けると整理しやすいです。

  • feature / preview: 古いデプロイは止めて、最新コミットだけ反映する
  • main / release/: 実行中デプロイは止めず、完走させる
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' && !contains(github.ref, 'release/') }}

ポイントは 2 つです。

  1. group に github.ref を含めると、ブランチごとに別の concurrency group になる。ある feature ブランチのキャンセルが、別ブランチや本番向け group を巻き込みにくい
  2. cancel-in-progress を式にすると、「どのブランチで実行中を止めるか」を 1 つの workflow 内で切り替えられる

開発用と本番用で workflow を分ける場合でも、この考え方(group の決め方 + キャンセル条件)は共通です。

従来のデフォルト(queue: single)

concurrency を指定しただけのデフォルトでは、待ち側の扱いは次のとおりです。

  • 同時実行は running 1 + pending 1 まで
  • 新しい run が同じ group に入ると、既存の pending はキャンセルされ、新しい run に置き換わる

つまり「順番待ちしてくれる」ように見えて、待ち行列の深さは 1 です。

図の状況を説明します。

  1. Deploy v1が本番へデプロイ中
  2. Deploy v2が pending で待機
  3. その直後に Deploy v3が到着すると、v2 はキャンセルされ v3 が待機になる
  4. v1 完了後に走るのは v3 だけで、v2 のデプロイは実行されない

main に短時間で複数回 merge すると、中間バージョンが未デプロイのまま終わる、という取りこぼしにつながります。「最新だけ出せばよい」なら問題になりにくい一方、「途中のリリースも順番に流したい」場合には向きません。

新機能 queue: max

2026年5月の変更で、同じ concurrency group に 複数の pending を滞留できるようになりました。

  • queue: single(デフォルト): pending は最大 1。追加が来ると既存 pending を置き換えキャンセル
  • queue: max: pending を 最大 100 まで滞留し、待ち始めた順(FIFO)に処理

queue: maxは待ちを開始した順に処理されるのが基本です。ただし開始時刻は前後しうるため、厳密な順序は保証されません(後述)

本番へ「1 本ずつ、かつ連続 merge もできるだけ順番に流す」用途に向いています。

on:
  push:
    branches:
      - main

concurrency:
  group: production-deploy
  queue: max

従来の「pending が 1 つしか置けず、後続到着で暗黙キャンセルされる」ケースが緩和されます。キューが満杯(100)のあとに来た run はキャンセルされます。

注意点として、queue: max と cancel-in-progress: true は併用できません。workflow の validation error になります。方針が対立するためです。

導入時の注意

  • 待ちが増えるトレードオフ: queue: max では中間バージョンも残る一方、古い run の完了まで後続の反映が遅れます
  • group の粒度: 環境単位にすると環境内は直列、ブランチ単位にするとブランチ間は独立、になります。切り方を間違えると、意図しない直列化やキャンセルが起きます
  • 順序の扱い: 公式ドキュメントによると、同じ group 内は「concurrency group の待ちを開始した時刻」に基づく FIFO で、dispatch 時刻そのものの厳密な保証ではありません
  • 上限: queue: max でも pending は 100 までです。それを超えた分はキャンセルされます

詳細は次を参照してください。

まとめ

concurrency は数行で書ける設定ですが、組み合わせによって挙動が大きく変わります。AI コーディングエージェントの活用などにより、短い間隔で commit や PR が作られる開発スタイルも増えています。そのような場合、同じ concurrency group に run が続きやすくなります。

  • どの concurrency group にまとめるか
  • 実行中の run を止めるか
  • 待ちを何件残すか。

この 3 点を図で押さえておけば、デプロイの取りこぼしや意図しないキャンセルを減らせると考え、この記事で整理しました。
環境とブランチの性質に合わせて、group と cancel / queue を設計するのが重要です。

AUTHOR
numa
numa
記事URLをコピーしました