# "Waiting for a runner to pick up this job": why GitHub Actions jobs stay queued, and a runner that's always there

> A GitHub Actions job stays queued when no free runner matches its runs-on labels: a wrong label, a self-hosted runner that is offline or busy, or the account's limit on jobs at once. Skipbox is a managed runner that is health-checked and rebuilt for you, for a flat $39 a month with unlimited minutes: at 20,000 minutes on 4 vCPU, $183 a month less than GitHub.

A queued job is a team waiting. The pull request can't merge, the deploy can't start, and someone keeps refreshing the run page. When it happens every week, because the self-hosted box went offline again or a burst of pushes hit the account's job limit, it costs more than any runner bill.

Skipbox is a GitHub Actions runner that is always there: your account's own machine, health-checked and rebuilt for you, for a flat $39 a month with unlimited minutes. Moving is one line, `runs-on: skipbox`.

- No runner to keep alive: Skipbox checks the machine after every job and rebuilds it when the check fails
- Skipbox jobs don't count against GitHub's limit on hosted jobs at once, and use no GitHub minutes
- 20,000 minutes a month on 4 vCPU: $222 on GitHub, $39 on Skipbox

**GitHub Actions at a flat price, unlimited minutes** Skipbox runs your GitHub Actions jobs on your own dedicated machine: $39 a month, however many minutes you run. Docker layers and dependency caches stay between jobs, so the next job starts right away.

A 4 vCPU runner, 20,000 minutes a month: GitHub $222 → Skipbox $39. You save $183 every month.

Moving takes one line: `runs-on: skipbox`.

[Start my 7-day free trial](https://app.skipbox.dev/sign-in?ref=g-github-actions-waiting-for-a-runner-en)

## Why the job is waiting

A job runs on a runner that carries every label in its `runs-on`. "Waiting for a runner to pick up this job" means there isn't a free one. In order of how often it's the cause:

| Cause | How to tell | Fix |
|---|---|---|
| The label matches no runner | A typo, or a retired image such as `ubuntu-20.04`. GitHub doesn't reject unknown labels; it waits for a runner that will never come | Correct the `runs-on` line |
| Your self-hosted runner is offline | Settings → Actions → Runners shows it as Offline | Restart the machine or the service |
| Your self-hosted runner is busy | It shows as Active. One runner runs one job at a time | Wait, or add runners |
| The runner isn't shared with the repository | It's Idle in the organization but missing from the repository's list | Add the repository to its runner group |
| The account's limit on jobs at once | 20 on GitHub Free, 40 on Pro, 60 on Team; queued jobs start as running ones finish | Fewer jobs at once, or runners outside the limit |

A job waiting for a self-hosted runner is cancelled after 24 hours, so a wrong label holds its run open for a day.

## What the waiting costs

The wait lands on people. A pull request that sits queued for 20 minutes is 20 minutes before review, and a self-hosted runner that dies overnight blocks the whole morning's merges until someone restarts it. That someone is an engineer who stops their own work to look after a CI box.

Keeping a self-hosted runner up is ongoing work: the machine, OS patches, runner application updates (an outdated runner stops taking jobs), a disk that fills with Docker images, and cleaning state between jobs so one job doesn't break the next.

Moving to GitHub-hosted runners trades that for a per-minute bill:

| Minutes a month (4 vCPU) | GitHub | Skipbox S | You save each month |
|---|---|---|---|
| 10,000 | $102 | $39 | **$63** |
| 20,000 | $222 | $39 | **$183** |
| 30,000 | $342 | $39 | **$303** |

GitHub's figures are after GitHub Team's included usage. Your own minutes go in the [calculator on the home page](https://skipbox.dev/?ref=g-github-actions-waiting-for-a-runner-en#cost).

And both get worse as runs multiply. Coding agents push at all hours, so more jobs arrive together and more of them arrive when nobody is watching the runner. A budget set to cap the per-minute bill stops jobs mid-month, which is another way to wait.

## What Skipbox takes off your hands

**A runner that stays up.** Each plan comes with your account's own machine. Skipbox provisions it, keeps the image updated, wipes the runner's credentials, processes and containers after every job, runs a health check, and rebuilds the machine from a clean image when the check fails. Nobody on your team restarts anything. From 13 November 2026, your checkout and its build output (Cargo's `target/`) stay on your own machine for your next jobs; Clean every job gives each job a fresh machine.

**Jobs at once you choose.** Skipbox jobs are self-hosted jobs to GitHub, so GitHub's limit on hosted jobs at once doesn't count them. On Skipbox your plans' vCPU set how many run together; a job past that waits its turn and starts as soon as one finishes, and your account's page shows how many wait and for how long. Add a plan to run more at once.

| Plan | Machine | Price |
|---|---|---|
| S | 4 vCPU · 16 GB | $39 a month |
| M | 8 vCPU · 16 GB | $79 a month |
| L | 16 vCPU · 32 GB | $149 a month |

**Shorter jobs, so shorter queues.** The machine keeps Docker layers, package caches and tools between jobs, so each job spends less time on setup and frees the runner sooner.

**No budget stops.** Minutes are unlimited, and Skipbox jobs use no GitHub minutes, so a GitHub budget never stops them.

## Moving is one line

```yaml
jobs:
  build:
    runs-on: skipbox # was ubuntu-latest or self-hosted
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test
```

1. Sign in to Skipbox with GitHub.
2. Install the Skipbox GitHub App and pick the repositories.
3. Change `runs-on` and push.

If a Skipbox job hasn't started, your account's page lists it as waiting or says why it wasn't run. A job that isn't there never reached Skipbox: check the label's spelling and that the app can use the repository. [A job stays queued](https://docs.skipbox.dev/job-queued) has the whole list.

## Security

- The machine is your account's alone. No other company's jobs run on it.
- After every job, the runner's credentials, processes and containers are wiped. From 13 November 2026, your checkout and its build output (Cargo's `target/`) stay on your own machine for your next jobs; Clean every job gives each job a fresh machine.
- Code from people without write access (such as pull requests from forks) runs on a fresh, throwaway machine.

The details are in [how jobs are isolated](https://docs.skipbox.dev/isolation).

## Try it

The trial is free for 7 days or 3,000 job-minutes, whichever comes first, and takes no card. Move the job that waits most often to `runs-on: skipbox` and push.

## FAQ

### Why is my GitHub Actions job stuck on "Waiting for a runner to pick up this job"?

No free runner carries every label in the job's runs-on. Either the label matches no runner (a typo, a retired image like ubuntu-20.04, a self-hosted runner that is offline or not shared with the repository), or every matching runner is busy, or the account is at its limit of jobs at once. Check the runs-on line first, then Settings, Actions, Runners.

### Self-hosted runner jobs are stuck in queued state and never start. What should I check and is there a managed alternative?

Check four things in order. In Settings, Actions, Runners: is the runner Offline (the machine or the runner service is down) or still Active (one runner runs one job at a time)? Does the runner carry every label in the job's runs-on? For an organization's runner, does its runner group include the repository? And is the runner application up to date, since one that falls too far behind stops processing jobs? The managed alternative is Skipbox: it runs your account's own machine, checks it after every job and rebuilds it when the check fails, for a flat $39 a month with unlimited minutes. Moving a job is one line, runs-on: skipbox.

### Why does my self-hosted runner keep going offline?

Because someone has to keep it running: the machine, the service, the runner application's updates and its disk. When any of them fails, jobs queue until a person notices. Skipbox runs the machine for you, checks its health after every job and rebuilds it from a clean image when the check fails.

### How many jobs can run at the same time on GitHub Actions?

On standard GitHub-hosted runners, 20 on GitHub Free, 40 on Pro, 60 on Team and 500 on Enterprise, across the whole account. Self-hosted runners, Skipbox's included, don't count against that limit. On Skipbox the jobs that run at once are set by your plans' vCPU, and a job past them waits its turn.

### How long can a GitHub Actions job stay queued?

A job waiting for a self-hosted runner is cancelled after 24 hours. So a job with a wrong label doesn't fail fast; it holds its run open for a day.

### Can I try Skipbox for free?

Yes. The trial is 7 days or 3,000 job-minutes, whichever comes first, with no card. Change one job's runs-on to skipbox and push.

Updated 2026-11-14 · https://skipbox.dev/guides/github-actions-waiting-for-a-runner
