Add a concurrency block with a group name to the workflow or job. GitHub then allows at most one running job or run in that group. Choose the group so related runs queue together, and think before setting cancel-in-progress, because cancelling a half finished remediation can leave a mess.
Why overlapping runs hurt
Security automation tends to write things: tickets, labels, comments, pull requests. Two runs of the same job at once can create duplicate tickets, race to push to the same branch, or post the same alert twice. Webhook retries and manual re-runs make overlap common, not rare.
The basics
GitHub's documentation says the default is to allow multiple jobs or runs at the same time, and that the concurrency key changes that. A group can be any string or expression, using the github, inputs, vars, needs, strategy and matrix contexts. Group names are case insensitive, so Prod and prod are one group.
concurrency:
group: sec-sync-${{ github.repository }}
cancel-in-progress: false
By default, when a run is queued behind a running one, it becomes pending, and a newer pending run replaces an older pending run. Setting cancel-in-progress: true also cancels the run that is currently executing.
The docs also describe a queue property. The default single keeps at most one pending run. The value max allows up to 100 pending runs, and anything past that is cancelled. The combination of queue: max with cancel-in-progress: true is rejected as a validation error. Ordering is first in, first out by the time each run started waiting, and the docs say exact ordering is not guaranteed.
Choosing cancel or queue
| Job type | Setting | Reason |
|---|---|---|
| Scan on pull request | cancel in progress true | Only the newest commit matters |
| Nightly report | cancel false | Let the running report finish |
| Ticket sync | cancel false, group per repository | Cancelling mid write can orphan tickets |
| Fix pull request creation | cancel false | Avoid a half pushed branch |
Rule of thumb: cancel jobs that only read and report on the latest code, and queue jobs that write.
Pick the group carefully
A group that is too broad serialises unrelated work. A group that is too narrow fails to prevent the clash. For a ticket sync, group by repository. For a workflow that updates one shared dashboard, use one fixed name. If runs triggered by different events must not overlap, put the same group string in both workflows.
Concurrency is not idempotency
Concurrency limits reduce collisions but do not guarantee exactly once. A run can be cancelled after it created a ticket but before it recorded that fact. Make the work itself safe to repeat: search for an existing ticket before creating one, and use a stable key. See search before you create and handling duplicate webhook events.
Quick check
Does concurrency work across different repositories?
The documentation describes groups within a repository, so do not rely on it to coordinate separate repositories. Use an external lock or a stable ticket key instead.
Should I cancel in progress for a scheduled report?
Usually no. Let the current report finish and let the next one wait.
Sources
Safeguard's policy gates run per pull request in CI, which is a natural place for latest commit wins. Try the free scan at gold.safeguard.sh/scan.
Last reviewed: 19 October 2026.