Safeguard
Developer Tools•

How do you stop a security automation job from running twice at once?

Use the GitHub Actions concurrency key so one run per group executes at a time. Group names, cancel in progress, queue options and when not to cancel.

Safeguard Security Team
3 min read

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 typeSettingReason
Scan on pull requestcancel in progress trueOnly the newest commit matters
Nightly reportcancel falseLet the running report finish
Ticket synccancel false, group per repositoryCancelling mid write can orphan tickets
Fix pull request creationcancel falseAvoid 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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.