Do Runner Groups Limit Concurrency?
No. A runner group decides which repositories may claim a set of runners, so a bad group leaves jobs unclaimed with an empty log rather than merely slow.
Answer
No. A runner group is an access boundary around a set of self-hosted runners: it decides which repositories may use them and, on GitHub Enterprise, which workflow paths, branches, tags, and commit SHAs may claim them, and no field in that access list holds a parallel job count, so adding or removing a group changes who may run while leaving the number of simultaneous jobs alone.
This access model is the one documented in the runner group checks of the common issues guide. The question keeps coming up because both problems land on the same screen. A misconfigured group shows up as jobs that never get claimed: the run sits queued, the log view stays empty, and it stays empty for as long as you let it sit, because no runner ever accepts the job and nothing is there to write. A capacity problem shows up as jobs that start late and then run normally, and it drains as other jobs finish. Duration is the tell. A group block survives an idle weekend, since the passage of time changes nothing about whether the repository appears on the group's allowed list.
On WarpBuild runners the capacity half of that pair is rarely the answer. Run as many jobs as your workflows need. Generally available Linux and Windows runners do not have plan-level concurrency caps, and the cloud runners documentation carries the exact scope: features that are Generally Available support unlimited concurrency on Linux and Windows runners, features in beta may carry limits, and macOS concurrency runs on a per-organization quota that [email protected] raises on request. A fresh virtual machine is provisioned per job, so there is no fixed pool to exhaust.
The three mechanisms that do cap parallel jobs are covered in the guide to GitHub Actions concurrency limits: the concurrency key you wrote in workflow YAML, the concurrent job ceiling attached to your GitHub plan, and the capacity of the runner pool behind your runs-on labels. A runner group sits in none of those three.
Detail
The two documented group checks
The common issues guide splits the checks by which group your runners registered into. Work them in this order.
| Check | Where it lives | What the run log shows when it fails |
|---|---|---|
| Bot has access to the repository | app.warpbuild.com/settings/account, Configure Runners on the GitHub connection | Every job in that repository stays queued with an empty log |
| Public repositories enabled on the default group | GitHub org settings, Actions, Runner groups, group id 1, Allow public repositories | Every job in every public repository stays queued, private repositories are unaffected |
| Repository access on a non-default group | GitHub org settings, Actions, Runner groups, the group selected in the WarpBuild dashboard | Jobs in repositories missing from the access list stay queued with an empty log |
| Workflow restriction on a non-default group | The same runner group settings page | One workflow file, branch, or tag stays queued while the rest of the repository runs normally |
runs-on label typo | The workflow file itself | The job stays queued and no group setting explains it |
WarpBuild registers as a self-hosted runner in the Default runner group (id 1) unless the organization selects another group. Confirm which group is in play at https://app.warpbuild.com/ci under Runners, then open the matching group in GitHub organization settings. Workflow restrictions are written as a full path with a ref, for example monalisa/octocat/.github/workflows/cd.yaml@refs/heads/main, which admits cd.yaml on main and queues every other workflow. Each entry is an allow-list rule, so the first entry you add excludes everything you did not list. The runner group glossary entry holds the plain definition if someone joining the thread needs it.
The repository access model, step by step
- Add the repository to the GitHub connection in the WarpBuild dashboard. A repository added after onboarding is the most common miss.
- If the repository is public, check
Allow public repositorieson the runner group. GitHub disables self-hosted runners in public repositories by default, including managed ones. - If your organization runs a non-default group, open that group in GitHub organization settings and confirm the repository appears on its access list. A group set to selected repositories blocks every repository that was never added to it.
- Read the workflow restriction list on the same page. An empty list admits every workflow. A list with one entry admits only that entry.
- Copy the
runs-onlabel from the pricing page rather than typing it, so a spelling slip does not imitate a group block.
Steps 2 through 4 are the ones that change per repository over time, which is why restricting which repositories use your runners belongs in an onboarding checklist rather than in incident response. The full walkthrough with screenshots lives in the guide to using runner groups with GitHub Actions.
Reading the difference in Queue Timings
The Queue Timings report breaks queue wait down per runner label and stack, with Run Count, Queue Time P75, and Queue Time P90 in the metrics table, a daily average chart, filters on runner label and stack, and CSV export. Every row needs a job that started, which is what makes the report a clean separator.
| What the report shows | Reading |
|---|---|
| Run Count flat on a label while the Actions tab lists queued jobs | Group access or label mismatch, since no job on that label ever started |
| Run Count climbing and P90 climbing with it | Capacity for that label |
| P90 spikes during working hours and settles overnight | Contention for a shared ceiling, usually the GitHub plan ceiling |
| P90 near zero on every label while pull requests still feel slow | Queue is healthy and job duration is the problem |
Two rows separate the group question from everything else. A group block cannot raise a percentile, because the jobs it blocks never produce a measurement. Anything that moves P90 is capacity or contention.
The probe, and what the minutes cost
Put a cheap identity step first in the job. When it never appears in the log, the job was never claimed, and the cause sits in the group rather than inside the workflow.
name: ci
on: [pull_request]
jobs:
test:
runs-on: warp-ubuntu-latest-x64-4x
steps:
- name: Print runner identity
run: echo "name=$RUNNER_NAME os=$RUNNER_OS arch=$RUNNER_ARCH"
- uses: actions/checkout@v4
- run: npm ci
- run: npm testA group restriction applies to every label inside the group at once, so splitting the workflow across platforms routes around nothing. Shapes and rates below come from the cloud runners documentation, checked on 2026-08-13.
| Runner label | OS | vCPU | RAM | Rate per minute |
|---|---|---|---|---|
| warp-ubuntu-latest-x64-4x | Ubuntu 24.04 | 4 | 16 GB | $0.008 |
| warp-ubuntu-latest-arm64-4x | Ubuntu 24.04 | 4 | 16 GB | $0.006 |
| warp-macos-15-arm64-6x | macOS 15 | 6 | 22 GB | $0.08 |
| warp-windows-latest-x64-4x | Windows Server 2022 | 4 | 16 GB | $0.016 |
Blocked jobs bill nothing, which is why a group problem hides for weeks. Once access is granted, warp-ubuntu-latest-x64-4x at 4 vCPU and 16 GB costs $0.008 per minute against $0.012 per minute for GitHub's 4-core Linux larger runner at the same shape, which is 33 percent lower list price (GitHub billing reference, checked on 2026-08-13).
Related Questions
Can a runner group cap the number of parallel jobs?
No. A runner group carries a repository access list and, on GitHub Enterprise, workflow path, branch, tag, and sha restrictions. None of those settings holds a parallel job count. The number of jobs that run at once comes from the concurrency key in your YAML, the concurrent job ceiling on your GitHub plan, and the capacity behind the runs-on label, all three of which are covered in the guide to GitHub Actions concurrency limits.
What does a runner group block look like in the run log?
The job stays queued and the log view stays empty for as long as you let it sit, because no runner ever claims the job and there is nothing to write. A capacity wait produces the opposite shape: the log fills in once a slot frees, usually within minutes, and the wait shows up as the gap between created_at and started_at on the job object. The common issues guide lists the group settings to check when the log stays empty.
How do I tell a group block from a capacity problem in Queue Timings?
Queue Timings reports Run Count, Queue Time P75, and Queue Time P90 for each runner label and stack combination. A blocked group never records a start, so the row for that label shows a flat Run Count while the Actions tab shows queued jobs. A capacity wait shows a Run Count that keeps climbing with P90 climbing alongside it. The runner group glossary entry and the guide to using runner groups with GitHub Actions cover the settings behind each reading.
Check per-minute rates for the labels above on the pricing page, then confirm your group change with the identity probe in a single pull request.
Start with $10 in free credits
Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.