GitHub Actions Stuck on Queued: Why Jobs Don’t Start
A job sitting in "queued" means no runner has picked it up yet. Here is why, in order of likelihood.
When jobs queue and never start, work down this list from most to least common.
Causes and fixes
| Cause | Fix |
|---|---|
| GitHub Actions incident | Check githubstatus.com - org-wide queuing is usually an outage |
| Concurrency limit reached | Other runs are using your concurrent-job allowance; wait or raise the plan |
| No runner matches the labels | A self-hosted/runs-on label has no online runner - fix the label or bring a runner online |
| Billing / spending limit | Hit the spending limit or unpaid invoice - runners pause until resolved |
| Larger-runner capacity | Specific larger-runner sizes can have brief capacity waits |
Reduce your exposure
Managed runners run on infrastructure independent of GitHub-hosted capacity and scale to your demand, so jobs are less likely to sit queued behind a capacity or concurrency wall.
Measure before you optimise
Pipeline optimisation usually targets the step people assume is slow. Get the real per-step timings first, because the answer is frequently dependency install or a cold cache rather than the build itself.
# per-job timings for the last 20 runs
gh run list --limit 20 --json databaseId,conclusion,createdAt,updatedAt \
--jq '.[] | "\(.conclusion)\t\(.createdAt)\t\(.updatedAt)"'
# per-step timing inside one run
gh run view <run-id> --log | grep -E "^\S+\s+.*Run |##\[group\]" | head -40Key takeaways
- Org-wide queuing = check status first.
- Single-workflow queuing = labels, concurrency, or billing.
- Independent managed runners reduce capacity waits.