Add a concurrency group to the workflow. A group allows at most one running run at a time, and by default a new run waiting in the group replaces any older pending one. Set cancel in progress to true only for pull request scans, where only the newest commit matters.
What GitHub documents
From the concurrency documentation:
- A group allows at most one running job or workflow run at a time.
- A run that arrives while another is running becomes pending.
- By default, a new pending run replaces an older pending run in the group, which is cancelled.
- With cancel in progress set to true, the run currently in progress is also cancelled.
- A queue setting of max lets up to 100 runs wait, and it cannot be combined with cancel in progress.
- Group names are case insensitive, and they should include the workflow name so one workflow does not cancel another.
Choose by scan type
| Scan | Setting | Reason |
|---|---|---|
| Pull request | Group per pull request, cancel in progress true | Only the latest commit matters |
| Nightly | One group, cancel in progress false | Let a running full scan finish |
| Release | One group per tag, no cancel | Every release needs its own result |
| Deploy rescan | Group per environment, queue max if every deploy must be checked | Keep an ordered record |
Examples
Pull request scans:
concurrency:
group: scan-${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
The fallback to the run ID matters. The docs warn that the head reference exists only for pull request events, so without a fallback the expression can fail on other events.
Nightly scans:
concurrency:
group: nightly-${{ github.workflow }}
cancel-in-progress: false
Steps
- List every workflow that runs a scan.
- For each, decide whether older runs are still useful.
- Write a group name that includes the workflow and the thing being scanned.
- Add a fallback for events that lack the property you use.
- Test by pushing two commits quickly and checking which run was kept.
Trade offs
Cancelling saves compute and keeps the pull request status honest, since a result for an old commit is noise. But cancelling a scheduled run mid way can leave you with no result for that night. For scheduled work, let it finish.
Note that ordering among waiting runs is not guaranteed, according to the docs, so do not build logic that depends on strict order.
Quick check
Will a new nightly run cancel the one in progress?
Not unless cancel in progress is true. By default it waits as pending, and replaces any older pending run.
Can two workflows share a group by accident?
Yes, if the names match. Include the workflow name in the group to scope it.
Sources
Last reviewed: 19 October 2026.