GitHub Actions concurrency group cancellation order during simultaneous workflow runs
0 reputation · 01 Dec 2025, 02:46 UTC
0 reputation · 01 Dec 2025, 02:46 UTC
Maintain zero‑downtime migration for a small application by using a single concurrency group to prevent overlapping deployments.
The GitHub Actions documentation states that a new run will cancel the currently running run within the same concurrency group, but it does not specify which run is cancelled when multiple runs are queued. Users report conflicting behaviors: some see the oldest running run cancelled, others observe the newest queued run being terminated first. This ambiguity can leave the application in an inconsistent state if the wrong run is cancelled mid‑deployment.
1. When several runs are queued in a concurrency group, which run does GitHub cancel first— the oldest running run or the newest queued run?
2. Is the cancellation order consistent across different event types (push, pull request, fork) and repository contexts?
3. Does GitHub provide any deterministic rule or configuration to control the cancellation priority within a concurrency group?
33775 reputation · 01 Dec 2025, 04:14 UTC
When you enable concurrency with cancel-in-progress: true, GitHub behaves as follows:
Because the running deployment is always the one that gets killed, you should design your deployment workflow to finish quickly or to be idempotent. If a queued run is cancelled before it starts, it simply never runs, so you don’t risk a partial deployment from that instance.
concurrency block to your workflow:
on: push
concurrency:
group: "deploy-${{ github.ref }}"
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: echo "Deploy step"
Run 1: started
Run 2: cancelled (running)
Run 3: cancelled (queued)
Run 4: cancelled (queued)
Run 5: startedcurl -H "Authorization: token $TOKEN" \
https://api.github.com/repos/OWNER/REPO/actions/runs | jq '.workflow_runs[] | {id, run_id, status, started_at, updated_at}'
Check that the run marked as cancelled has the earliest started_at among the running runs, and that queued runs are cancelled in reverse order of created_at.Since GitHub does not expose a priority setting, the typical workaround is to split the deployment into two distinct concurrency groups: one for the “deploy” job and another for any ancillary tasks. Alternatively, you can add a lightweight “queue” job that checks a lock file in a shared storage (e.g., S3 or a database) and only proceeds when it can acquire the lock, thereby serialising deployments at the application level.
To tailor the recommendation further, could you let us know whether your workflow triggers are limited to a single branch or span multiple branches/tags? The cancellation scope is per‑ref, so the behaviour can differ if you’re running deployments on several branches concurrently.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.