How Fast Does a Runner Start After Queueing?
Start time is the gap between a job entering the queue and its first step running. Read it per runner label in the Queue Timings report, then cut it.
Last verified:
Start time is the gap between a job entering the GitHub Actions queue and the first step of that job running on a machine, and WarpBuild publishes it per runner label as queue time P75 and P90 in the Queue Timings report (reports documentation). The documented figures are about 15 seconds for a BYOC runner backed by a standby disk (standby disks documentation) and 45 to 60 seconds for a snapshot runner booting from saved VM state (snapshot runners documentation), and Braintrust reports consistent sub-minute runner provisioning on both ARM64 and x86 WarpBuild runners (Braintrust customer page).
Answer
Three timestamps bound the number. GitHub records the moment the job is queued, the runner platform records the moment a machine is assigned and finishes booting, and the job log records the first step of your own work. Start time covers the two gaps before that first step. Run time is everything after it. A single elapsed figure on the run page adds them together and hides which one moved.
Here is what each path to a machine documents.
| Path to a machine | Documented start time | Source |
|---|---|---|
| WarpBuild Cloud runner, ephemeral VM per job | Braintrust reports consistent sub-minute provisioning on ARM64 and x86 | Braintrust customer page |
| BYOC runner with a standby disk available | about 15 seconds | standby disks documentation |
| BYOC runner with no standby disk free | a new VM is created for the job, and the pool refills within about 1 minute | standby disks documentation |
| Snapshot runner booting from saved VM state | 45 to 60 seconds | snapshot runners documentation |
Every one of them is allocated as an ephemeral VM that is created when a job needs it and destroyed when the workflow completes (cloud runners documentation). That design is why the number above is a provisioning time rather than a wait for a free slot in a fixed pool.
Those figures hold only while a machine is available to allocate. Run as many jobs as your workflows need. Generally available Linux and Windows runners do not have plan-level concurrency caps, so a pull request that fans out to 30 shards does not turn the last shard's start time into a wait behind the first 29. Where a fixed ceiling does exist, the wait lands in the same queue time column, and the guide to GitHub Actions concurrency limits covers the ceilings that produce it.
One shape of start time has nothing to do with capacity. A job whose runs-on label set matches no online runner stays queued until GitHub cancels it at 24 hours (GitHub Actions limits, checked on 2026-08-13). A start time measured in hours is almost always that, and no amount of capacity changes it.
Detail
Reading start time per label, separated from run time
Two reports carry the split.
- Open the Queue Timings report and set a date range. Each row is a unique runner label and stack combination with run count, queue time P75, and queue time P90. The daily bar chart plots average queue time next to total job count per day.
- Open the Jobs report for the same range. Each row is a unique repository, workflow, and job name combination with run count, success rate, duration P75 and P90, queue time P75 and P90, and CPU and memory percentiles.
- Compare the two columns for the same job. Queue time moving while duration stays flat is a start time problem. Duration moving while queue time stays flat is a build problem, and nothing on this page applies to it.
- Export both tables to CSV. The same export after a change is the before and after record, and the CSV includes every row matching the current filters rather than the current page only.
Queue time is the only number here that a workflow file cannot change. The steps you write start counting after it ends.
What raises start time
Capacity that has to be created. On BYOC, WarpBuild checks for an available standby disk when GitHub requests a runner. If one is free it is powered on and the job starts in about 15 seconds. If none is free, a new VM is created for the job and pays a full allocation and boot, and the control plane refills the pool within about a minute of reconciliation time (standby disks documentation). The documented guidance is to size the pool from the number of jobs you expect to run concurrently on that runner type, so an eight shard matrix wants at least eight disks. Spot instances are not supported for standby disks, because a spot instance can be reclaimed at any time.
Large custom VM images. A BYOC custom image is built in your own cloud account and the runner boots from it, so the image contents and the instance type it boots on set the floor for allocation and boot (custom VM images documentation). Anything baked into the image is paid once at build time instead of once per job, which is the trade that makes a larger image worth a slower boot.
Cold image pulls. A job using the container: key or a services: image pulls that image before your first step runs. Runner storage is ephemeral and is deleted when the runner terminates (cloud runners documentation), so the pull happens on every run. This one is worth calling out because it does not show up in the Queue Timings report at all: queue time stays flat and the first real step simply lands later. Read it from the container initialization step in the job log.
Snapshot boot. Snapshot runners boot from saved VM state in 45 to 60 seconds, which is slower than a default runner boot (snapshot runners documentation). The extra seconds buy a machine that already has the toolchain, dependencies, and prior build output on disk, so the comparison worth making is total time to the first useful step rather than time to the first step.
A worked example: eight shards on a BYOC runner type
Take one repository running 120 pull request workflows per weekday, each fanning out to eight test shards on a single BYOC runner type. Measure queue time P75 for that label in the Queue Timings report before changing anything, then compare against the documented standby figure.
| Line | No standby disk free | Standby disk available |
|---|---|---|
| Start time per shard | 90 seconds (measured P75) | about 15 seconds |
| Front of a pull request run | 90 seconds | about 15 seconds |
| Shard starts per weekday | 960 | 960 |
| Aggregate start time per weekday | 24.0 hours | 4.0 hours |
| Developer visible wait per weekday | 3.0 hours | 0.5 hours |
The 90 second figure is a placeholder for your own P75. The arithmetic is what carries over: 960 shard starts times 75 seconds saved is 72,000 seconds, or 20 hours of aggregate machine wait removed per weekday, and 120 runs times 75 seconds is 2.5 hours of wait that developers actually watch. Sizing the pool at eight disks is what makes the second column true for every shard rather than the first one only. WarpBuild does not charge for standby disks; your own cloud account pays for about 90 seconds of VM time while each disk initializes and for the network disk while the VM sits shut down.
The workflow side of this needs no change at all. The label is the only lever a workflow file holds.
name: ci
on:
pull_request:
branches: [main]
jobs:
tests:
runs-on: warp-ubuntu-latest-x64-4x
timeout-minutes: 25
strategy:
fail-fast: true
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm test -- --shard=${{ matrix.shard }}/8Everything that sets the start time for this matrix is a property of the runner behind warp-ubuntu-latest-x64-4x, which is why the measurement belongs in the reports view and the fix belongs in the runner configuration.
What the wait costs
Start time is wall clock rather than runner configuration, and it compounds across every shard of every run. Execution minutes bill at the rate for the label: warp-ubuntu-latest-x64-4x is $0.008 per minute and warp-ubuntu-latest-arm64-4x is $0.006 per minute, with the full list on the pricing page.
Related Questions
Where do I read start time for one runner label?
The Queue Timings report. Each row is a unique runner label and stack combination with run count, queue time P75, and queue time P90, and a daily bar chart shows average queue time next to job count per day. Every table exports to CSV, so the same label before and after a change is a two column comparison. The measurement itself is defined in queue time.
Why do some GitHub Actions jobs start in seconds and others wait minutes?
Either a machine has to be built for the job, or no machine carrying every label in runs-on is free. Generally available WarpBuild Linux and Windows runners do not have plan-level concurrency caps, so check the labels and runner-group access when one of those jobs never finds a match. GitHub cancels a queued job after 24 hours (GitHub Actions limits, checked on 2026-08-13). The guide to GitHub Actions concurrency limits covers the ceilings that do apply.
Does a fast start remove the rest of the setup time?
No. Allocation and boot are the first two costs of a cold start, and toolchain install, dependency restore, and first-run compilation follow them inside your own steps. Standby disks start a BYOC job in about 15 seconds and leave those three untouched, while a snapshot runner boots in 45 to 60 seconds with the toolchain and prior build output already on disk. The guide to removing cold starts from GitHub Actions jobs measures all five parts separately and matches each lever to the parts it removes.
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.