Skip to content
Latchkey

GitHub Actions concurrency deadlock between deploy jobs

A concurrency group serializes runs, but if a dependent job needs another job that is itself queued behind the same group, the chain can stall. Misusing cancel-in-progress across coupled deploy jobs produces an apparent deadlock.

What this error means

Two deploy jobs sit pending indefinitely, each effectively waiting for the other because they share a concurrency group with a dependency between them.

github-actions
Waiting for a deployment slot in concurrency group "deploy-production"
(job "promote" pending behind "deploy", which needs "promote")

Common causes

Coupled jobs share one concurrency group

Jobs linked by needs are placed in the same group, so one cannot start until the other releases the slot it is also waiting on.

cancel-in-progress canceling the wrong run

cancel-in-progress cancels an in-flight run that a dependent job relies on, leaving the pipeline stuck.

How to fix it

Separate the concurrency scope

  1. Put serialization at the workflow level for a single deploy pipeline, not on individual coupled jobs.
  2. Give independent pipelines distinct concurrency group keys.
  3. Use cancel-in-progress only where superseding an older run is actually safe.
.github/workflows/deploy.yml
concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: false

How to prevent it

  • Scope concurrency to a whole pipeline rather than to interdependent jobs.
  • On Latchkey managed runners, jobs stuck purely on transient slot contention are retried automatically; a true config deadlock still needs the scope fix above.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →