What Is a Concurrency Group in GitHub Actions?
Last updated
A concurrency group is a named key in a GitHub Actions workflow or job that allows only one run with that key at a time. Newer runs wait, or, with cancel-in-progress: true, cancel the older run that is still going.
How it works
You add a concurrency block with a group string. GitHub evaluates the string for each run. If another run with the same group is in progress, the new run is held as pending. When cancel-in-progress is true, the old run is cancelled instead and the new one starts immediately.
Only one run can be pending per group. If a third run arrives while one runs and one waits, the waiting run is dropped in favour of the newest, unless you cancel in progress.
The group string usually combines the workflow name and a ref, so each branch or pull request has its own lane. Cancelling superseded runs on a busy pull request is one of the easiest ways to cut wasted CI minutes.
Example
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
Here every pull request gets its own group and old runs are cancelled on a new push. Pushes to main get a group by ref, and are queued rather than cancelled, so every commit on main is tested.
It also works at job level, for example to serialise deploys:
jobs:
deploy:
concurrency:
group: production-deploy
cancel-in-progress: false
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
Common mistakes
- Cancelling deploys halfway. A cancelled deploy can leave infrastructure in a bad state. Use
cancel-in-progress: falsefor deploys. - Group too broad. A fixed string like
ciacross all branches makes unrelated pull requests cancel each other. - Cancelling main. Cancelling runs on the default branch hides which commit broke the build.
- Cancelling merge queue runs. Runs from the
merge_groupevent should not be cancelled; see merge queue. - Forgetting the workflow name. Two different workflows with the same group share a lane and cancel one another.
Related terms
To trim spend further, see reduce your GitHub Actions bill.
FAQ
What is the difference between workflow and job concurrency?
Workflow-level concurrency controls whole runs. Job-level concurrency controls a single job, useful for locking a shared resource such as a deployment target.
Does cancel-in-progress save money?
Yes. Superseded runs stop consuming minutes. The saving depends on how often you push to open pull requests.
Are cancelled runs billed?
Minutes used before cancellation are billed. Minutes after cancellation are not.
Related
- What Are CI Minutes? How Billing WorksCI minutes are the billed compute time your pipelines use. Learn how GitHub Actions counts minutes, multipliers, rounding and how to reduce usage.
- What Is a Cache Key in GitHub Actions?A cache key is the string that identifies a saved cache in GitHub Actions. Learn how keys and restore-keys work and how to avoid stale caches.
- What Is a Flaky Test? Definition and CausesA flaky test passes and fails on the same code without any change. Learn the common causes, how to detect them in CI and how to fix or quarantine them.
- What Is a Matrix Build in GitHub Actions?A matrix build runs one job many times with different inputs like OS or language version. Learn the syntax, include/exclude and common mistakes.