Overview
When a job sits in a "Not Running", "Queued", or prolonged "Preparing Environment" state, it almost always means your organization has reached a concurrency limit. This article explains what concurrency limits apply to your plan, how to identify what is consuming your concurrency, and how to resolve the issue.
Understanding Concurrency Limits by Plan
Every CircleCI plan has a maximum number of jobs that can run simultaneously. When that limit is reached, new jobs wait in the queue.
| Plan | Concurrent Job Limit | Symptom |
|---|---|---|
| Free | 30 jobs | Job shows "Not Running" |
| Performance | 80 jobs | Job shows "Not Running" |
| Scale / Custom | Configurable (default 200) | Job stays in "Preparing Environment" for >5 minutes |
Self-hosted runners have a separate limit. The
maxConcurrentTaskssetting on your runner resource class caps how many jobs a runner will claim simultaneously, independent of your plan's job concurrency. See Step 5 below.
Step 1: Check Your Current Concurrency Usage
There is no real-time concurrency view in the CircleCI UI, but you have two options:
Option A — Concurrency Snapshot Script (Cloud only)
Use the Current Concurrency Snapshot script to get a point-in-time view of all running jobs in your org and which resource classes they are using. Run it while jobs are queuing to see what is consuming capacity.
Option B — CircleCI API
Query the running workflows across your org using the CircleCI v2 API with a personal API token:
curl -G "https://circleci.com/api/v1.1/recent-builds?shallow=true&limit=100" \
-H "Circle-Token: <YOUR_API_TOKEN>" | jq '.[] | select(.status == "running") | {build_url, queued_at, build_num}'Step 2: Check for Forgotten SSH Jobs (Most Common Cause)
SSH jobs are the #1 hidden cause of concurrency exhaustion. Once you navigate away from a running SSH job, it disappears from the pipeline view — but it is still running and consuming a concurrency slot.
- An SSH job stays available to connect for 10 minutes after it finishes.
- Once you connect, the session stays open for up to 2 hours.
- During all of that time, the job counts against your concurrency limit.
How to find and cancel SSH jobs:
Navigate to the legacy jobs view for each project where SSH reruns may have been triggered:
Replace {vcs} with gh or bb, and fill in {org} and {project}.
SSH jobs will be visible here even when they don't appear in the pipelines view. Click into any running SSH job and cancel it from the Rerun menu.
You can also find all running SSH jobs across your org via the API:
curl -G "https://circleci.com/api/v1.1/recent-builds?shallow=true" \
-H "Circle-Token: <YOUR_API_TOKEN>" | jq '.[] | select(.why == "ssh") | select(.status == "running") | {build_url, build_num}'Prevention: Always manually cancel an SSH job when you finish debugging. Go to Project Settings → SSH → Disable rerun with SSH if SSH reruns are not needed for a project.
Step 3: Check for Invalid or Misconfigured Runner Resource Classes
If your configuration references a runner resource class that has no active runners attached to it — due to a typo, a decommissioned machine, or a runner that hasn't checked in — jobs will queue against that resource class indefinitely. Those queued jobs count toward your concurrency limit, causing other jobs to queue even though nothing appears to be running.
How to check:
- Go to Org Settings → Self-Hosted Runners and inspect the inventory for each resource class referenced in your
config.yml. - Any resource class showing "No Runners" or "No Tasks" is a candidate.
- Verify the resource class name in your config exactly matches the name in the runner inventory (names are case-sensitive).
# Example — verify this matches exactly in the runner inventory resource_class: my-org/my-runner-name
Cancel any jobs queued against invalid resource classes to immediately free up concurrency.
Step 4: Check for Unexpectedly High Parallelism
A single job configured with a high parallelism value can consume a large portion of your concurrency budget instantly. A common typo — for example, parallelism: 99 instead of parallelism: 9 — can spike usage and queue everything else.
How to check:
Search your config.yml files across all active projects for the parallelism key:
# Each parallel "slice" counts as one concurrent job against your limit parallelism: 9 # 9 concurrent jobs — intended parallelism: 99 # 99 concurrent jobs — likely a typo
Also review any scheduled pipelines and automations. If multiple scheduled triggers fire at the same time (for example, a burst of delayed VCS webhooks), they can all start high-parallelism jobs simultaneously.
Step 5: Self-Hosted Runner — maxConcurrentTasks Limit
If you are using self-hosted runners (machine or container runner), concurrency is also controlled by the maxConcurrentTasks setting on the resource class, independent of your plan's job limit. If this is set too low, jobs will queue at the runner level even if your plan limit has not been reached.
The default is 20 concurrent tasks for most plans. Check your plan's runner inventory page:
If you are seeing queued jobs but your plan's concurrency limit is not the constraint, the runner's maxConcurrentTasks cap is likely the bottleneck. Contact support to request an increase.
Step 6: Request a Concurrency Limit Increase
If you have ruled out the causes above and are legitimately hitting your plan's job concurrency ceiling, you can request an increase:
- Scale and Custom plan customers: Contact CircleCI support and include the output from the Concurrency Snapshot Script and a description of your typical pipeline patterns. We can increase your concurrency limit.
- Performance plan customers: Performance plans have a fixed 80-job limit. To go beyond this, you will need to upgrade to a Scale or Custom plan. Contact your account team or CircleCI Sales.
- Free plan customers: Free plans are capped at 30 jobs. Upgrading to Performance or Scale is the path to higher concurrency.
Preventing Future Queuing
| Action | Impact |
|---|---|
| Cancel SSH jobs immediately after debugging | Frees 1–2 hours of concurrency per job |
Set explicit parallelism values; review regularly | Prevents runaway parallelism from a typo |
| Stagger scheduled pipelines | Avoids burst triggers that simultaneously consume concurrency |
| Use the Concurrency Snapshot during peak hours | Gives early warning before limits are hit |
| Audit runner inventory after decommissioning machines | Prevents ghost resource classes from counting against limits |
Comments
Article is closed for comments.