How can I eliminate GitHub Actions queue times?

Switch jobs to GitHub Actions runners managed by Depot to remove queue times caused by runner capacity. Depot has no concurrency limits and provisions a fresh, single-tenant runner for every job.

Last updated 2026-09-10

To eliminate GitHub Actions queue times caused by runner capacity, switch your jobs to GitHub Actions runners managed by Depot.

Depot has no concurrency limits. When GitHub sends Depot a job, Depot assigns a fresh runner from a pre-provisioned standby pool. Each job runs on its own single-tenant virtual machine, and that machine is destroyed after the job finishes.

That removes the capacity queue. A burst of 50 ready jobs can fan out across 50 runners instead of waiting for a small pool of available machines.

It doesn't remove every kind of wait inside GitHub Actions. A job can also be blocked by another job, a workflow concurrency rule, an environment approval, an incorrect runner label, or GitHub's control plane. Those waits need a different fix.

Move jobs to Depot GitHub Actions runners

After connecting a GitHub organization to Depot, change the runs-on label in the workflow:

 jobs:
   test:
-    runs-on: ubuntu-24.04
+    runs-on: depot-ubuntu-24.04

The rest of the workflow stays the same. Existing actions, secrets, permissions, matrices, and pull request checks continue to work through GitHub Actions.

The runners are ephemeral, but provisioning isn't instantaneous. Typical startup time is 10 to 45 seconds. Depot usually allocates the underlying compute in about 15 seconds, while runner registration and job dispatch depend on GitHub's control plane. Depot doesn't bill for provisioning time.

You can inspect that timing breakdown in the graph at the top of the GitHub Actions page in the Depot dashboard. If allocation regularly takes longer than one minute during normal operation, contact Depot support.

Identify the wait before changing the workflow

GitHub can show a job as queued or waiting for several different reasons:

CauseWhat is happeningFix
Runner capacityThe job is ready, but no matching runner is availableMove the job to Depot
Job dependencyA job named in needs hasn't finishedRemove unnecessary dependencies or split the dependency graph
Workflow concurrencyAnother run holds the same concurrency groupNarrow, remove, or intentionally cancel the conflicting group
Environment protectionA deployment is waiting for approval, a wait timer, or another protection ruleReview the repository environment rules
Runner label or group accessGitHub can't find an eligible runner for the requested labelsCorrect the label and allow the repository to use the default runner group
Organization usage capA self-imposed Depot usage limit prevents another job from startingRaise or remove the cap in Depot organization settings
GitHub control-plane latencyGitHub hasn't delivered, registered, or dispatched the jobCheck GitHub status and compare webhook, allocation, registration, and dispatch timing

Changing runner providers only addresses the first row. That's often the largest source of queue time during a burst, but treating every wait as a capacity problem leads to the wrong fix.

Remove accidental concurrency locks

GitHub's concurrency setting deliberately limits how many runs or jobs sharing a key may proceed. This is useful for deployments and other work that must not overlap. It can also serialize an entire repository by accident.

For example:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

This allows only one active run for the same workflow and ref. A new run cancels the one already in progress. If the work can safely overlap, remove the concurrency block or make the group more specific. Keep it when serialization is protecting a real shared resource.

The same rule applies to deployment environments. Required reviewers and wait timers are gates, not runner queues. Faster runner provisioning shouldn't bypass them.

Parallelize jobs that are actually independent

Unlimited runner concurrency matters only if the workflow exposes parallel work. A single job with 20 sequential steps still uses one runner.

Split independent test suites into jobs or matrix shards so GitHub can dispatch them at the same time:

jobs:
  test:
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    runs-on: depot-ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - run: pnpm install --frozen-lockfile
      - run: pnpm test --shard=${{ matrix.shard }}/4

Depot can provision a separate runner for every shard without a concurrency cap. This usually shortens wall-clock time, but it can increase total compute usage. Parallelize work that shortens the critical path, not every step that can be turned into another job.

Test queue behavior at peak load

An empty runner pool at 2 AM doesn't tell you what developers see after ten pull requests land together.

Measure queue and startup time during the busiest part of the engineering day. Use the same workflow, commit, runner label, and matrix width for every provider. Record p50 and p95 from the moment each job becomes ready until its first user step starts.

Then dispatch a controlled burst that resembles production traffic. If the normal peak is 40 ready jobs, test 40 ready jobs. Watch for:

  • Jobs that wait because the provider has a concurrency quota or no matching capacity
  • Provisioning that gets slower as the burst grows
  • GitHub registration and dispatch delay after the runner is ready
  • Accidental serialization from needs, concurrency, or environment gates
  • Failed provisioning and automatic retries hidden inside one final status

Depot has no runner concurrency limit, so Linux and Windows jobs can scale with the exposed parallelism in the workflow. macOS uses a fixed-capacity pool because of Apple's licensing constraints and should be measured separately.

Keep the raw runs. Queue behavior is a distribution, not one screenshot. The result that matters is the p95 wait under the load your team actually creates.

Fix jobs that stay queued indefinitely

If a Depot job never starts, capacity may not be the problem. Check these configuration details:

  1. Use one Depot GitHub Actions runner label, such as depot-ubuntu-24.04-4.
  2. Don't combine it with self-hosted in a label array.
  3. Confirm the Depot Managed Runners GitHub app can access the repository.
  4. Confirm the repository is allowed to use the organization's default runner group.
  5. Check whether the organization has reached a self-imposed usage cap.
  6. Check Depot status and GitHub status for an incident.

Depot GitHub Actions runners require a repository owned by a GitHub organization. They aren't available for repositories owned by an individual GitHub account.

Linux and Windows runner capacity is elastic. macOS capacity isn't fully elastic because of Apple's licensing constraints, so macOS jobs can still see longer queue times during periods of high demand.

For current labels, machine sizes, and platform constraints, see Depot GitHub Actions runner types.

Start building with Depot
in minutes