---
title: "Cancel superseded CI runs: GitHub Actions concurrency"
description: "Add a concurrency group with cancel-in-progress so a new push cancels the obsolete run instead of piling up runners. Copyable GitHub Actions YAML."
url: https://starsling.dev/best-practices/github-actions/cancel-superseded-runs
canonicalUrl: https://starsling.dev/best-practices/github-actions/cancel-superseded-runs
---

# Cancel superseded runs with concurrency groups

Best practice: Trigger Scope. Last updated: 2026-07-06

Add a GitHub Actions `concurrency` group scoped to the ref with `cancel-in-progress: true`, so a new push cancels the now-obsolete run instead of leaving both to occupy runners.

## Do this

PR workflows declare a top-level `concurrency` group keyed on the workflow and ref, with `cancel-in-progress: true`. When a developer pushes again, the superseded run is cancelled instead of running to completion for a commit no one is waiting on. The group is broad enough (per-workflow-per-ref) that it doesn't serialize unrelated jobs.

_One live run per ref; cancel superseded PR runs_

```yaml
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  # Cancel superseded runs on PRs; let main / release runs finish.
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
```

## Avoid this

Each new push leaves the old run going, so obsolete commits keep occupying runners.

_No concurrency: obsolete runs pile up_

```yaml
# No concurrency block. Every push to a PR starts another full run,
# and the superseded ones keep occupying runners until they finish.
```

## Why it matters

Without a concurrency group, rapid pushes to a PR leave several full runs racing, every obsolete one still burning a runner until it ends. Cancelling superseded runs reclaims that compute and frees capacity for runs that matter. Keep the group scoped to the ref, not a global constant, an over-narrow or over-broad group is its own problem (it can serialize or cancel jobs you didn't mean to).

## When to use

**Use it when:** PR- and branch-push workflows where developers commonly push several times in quick succession.

**Be careful when:** Do not use `cancel-in-progress: true` on the default branch or release workflows where every commit must complete (each may deploy or publish). Scope cancellation to PRs; let `main` runs finish.

## Verify on your repo

Hand this prompt to your coding agent to audit and fix this practice in your own repo:

Inspect this repo's .github/workflows for a top-level `concurrency:` block on `pull_request` and `push` workflows. Flag any that have none, or one with `cancel-in-progress: false`, so obsolete runs pile up on rapid pushes. Add a `concurrency` group keyed on `${{ github.workflow }}-${{ github.ref }}` with `cancel-in-progress` scoped to PRs, and make sure the default branch and release workflows still let every run finish (never set `cancel-in-progress: true` on `main`). Confirm the group includes `github.ref` so it does not serialize unrelated PRs into one lane. Show me the diff and open a PR rather than applying it blindly.

Prefer to check by hand:

- Check PR-triggered workflows for a top-level `concurrency:` block: `grep -rn 'concurrency' .github/workflows/`.
- Flag `pull_request`/`push` workflows with no concurrency group, or one with `cancel-in-progress: false`.
- Confirm the group key includes `github.ref` (per-ref), not a global constant that would serialize every PR into one lane.

## FAQ

### Where does the concurrency block go, workflow or job level?

Put it at the top level of the workflow so it governs the whole run. Job-level concurrency is for finer control, but for cancelling superseded PR runs the workflow-level group keyed on `github.ref` is what you want.

### Is it safe to cancel in-progress runs on main?

No. On the default branch or a release workflow, every commit typically needs its run to complete, cancelling could skip a deploy or publish. Scope `cancel-in-progress: true` to PR refs and let main runs finish.

## More best practices for GitHub Actions

- [Cut CI queue time in GitHub Actions](https://starsling.dev/best-practices/github-actions/cut-queue-time)
- [Filter workflows by path in GitHub Actions](https://starsling.dev/best-practices/github-actions/path-filter-workflows)
- [All CI best practices](https://starsling.dev/best-practices/github-actions)

## Sources

1. [GitHub Actions - concurrency](https://docs.github.com/en/actions/concepts/workflows-and-actions/concurrency)
