Why Do Windows Runners Take Longer to Start?
Windows GitHub Actions runners start slower because the image is larger and the boot path does more work, so every job pays a higher fixed cost.
Last verified:
Windows GitHub Actions runners take longer to start than Linux runners because the Windows image is larger and the boot path does more work before the runner agent is ready to accept a job. That cost is fixed per job rather than proportional to the work a job does, so the lever that moves it is the number of Windows jobs a workflow starts rather than the contents of each one.
Answer
WarpBuild provides Linux x64, Linux ARM64, macOS, and Windows runners, and every WarpBuild runner is a fresh ephemeral virtual machine per job, per the cloud runners documentation. A Windows job and a Linux job therefore pay the same kind of bill: boot a machine, register it, then run steps. The Windows side of that bill is larger at every stage, and the catalog shows one reason directly. Windows shapes carry a 256GB SSD volume while the Linux x64 shapes carry 150GB, and the Windows image ships the same tooling as the GitHub-hosted Windows runners, which is a Visual Studio and .NET toolchain rather than an apt tree.
Start time splits into three phases. Only the third one is under the control of the workflow file.
| Phase | What happens | What moves it |
|---|---|---|
| Image boot | A fresh Windows Server virtual machine boots from the runner image and brings up its services | The image itself. No workflow change touches this phase |
| Runner registration | The runner agent starts, registers for the label set in runs-on, and takes the queued job | Nothing in the workflow file |
| First-step warm up | actions/checkout, toolchain setup, and package restore run against a cold 256GB volume with antivirus scanning files as they land | Checkout depth, cache configuration, and how much restore work the first steps do |
Phases one and two are why a Windows job that does nothing still bills more than a Linux job that does nothing. Phase three is where most teams lose the largest block of time, because Windows jobs restore packages over the network on every run: WarpBuild caches are not supported on Windows-based runners, so Windows jobs use actions/cache the same way they would on a GitHub-hosted Windows runner.
The full Windows label list, image families, and shapes are on the Windows runners page.
Detail
Measure it before changing anything
The number most teams quote for start time is the gap between pushing a commit and seeing the first log line, and that gap contains two separate things: queue wait, which depends on capacity, and startup, which depends on the platform. Separate them with the reports documentation surfaces.
- Open the Queue Timings report and set the range to 30 days. Each row is a unique runner label and stack with run count, queue time P75, and queue time P90. Record the rows for your
warp-windows-labels and for your Linux labels. - Open the Jobs report for the same range. Each row is a unique repository, workflow, and job name combination with duration P75 and P90 alongside queue time P75 and P90.
- Run a probe workflow so that one row in the Jobs report contains startup and nothing else.
name: runner-start-probe
on:
schedule:
- cron: "0 * * * *"
workflow_dispatch:
jobs:
probe:
strategy:
fail-fast: false
matrix:
runner:
- warp-ubuntu-latest-x64-4x
- warp-windows-latest-x64-4x
runs-on: ${{ matrix.runner }}
steps:
- run: echo "runner ready"The probe job has one step, so its duration P75 in the Jobs report is the fixed cost of starting a job on that label, and the difference between the two legs is the platform-dependent part. Queue time for the same rows comes from Queue Timings, which keeps capacity out of the measurement. Export both tables to CSV and keep them as the before record.
Hourly probes produce about 720 jobs per leg per month. At one billed minute each that is 720 x $0.016 = $11.52 on the Windows leg and 720 x $0.008 = $5.76 on the Linux leg.
Continuous versions of these numbers come from CI observability in the WarpBuild product surface, which also covers snapshot runners, remote Docker builders, an MCP server, and the Action Debugger. Snapshot runners are the usual answer to a slow boot, and they do not apply here: they are supported only on WarpBuild Cloud Ubuntu runners, and snapshot labels on a Windows runner are silently ignored.
The workflow-level fix: fewer, larger Windows jobs
Because startup is charged per job start, a wide matrix of short Windows jobs pays for it repeatedly. Billing is per minute of runner time, and the 2 vCPU Windows shapes were removed on June 8, 2026, so 4 vCPU is the smallest shape a short Windows job can start on.
Write S for the startup minutes you measured with the probe and W for the work minutes per unit of work. A matrix of N short jobs bills N x (S + W). Consolidating the same work into N/5 jobs bills (N/5) x (S + 5W), and the difference is pure startup.
Worked with S = 1.5 minutes and W = 2 minutes, on warp-windows-latest-x64-8x at $0.032 per minute, for a lane that runs 200 times per month:
| Job shape | Jobs per run | Billed minutes per run | Monthly minutes | Monthly cost |
|---|---|---|---|---|
| 40 short jobs | 40 | 40 x 3.5 = 140 | 28,000 | $896.00 |
| 8 consolidated jobs | 8 | 8 x 11.5 = 92 | 18,400 | $588.80 |
| Difference | 32 fewer starts | 48 minutes | 9,600 | $307.20 |
Substitute your own S and W from step 3 above. The rule the table encodes is that consolidation is worth doing when startup is a large share of billed minutes per job, which at S = 1.5 and W = 2 is 43 percent. It also has a wall-clock cost: 40 short jobs running in parallel finish in about 3.5 minutes, while 8 consolidated jobs finish in about 11.5 minutes. Consolidation wins on the bill and on queue pressure, and it loses on wall clock whenever the short jobs really were running in parallel. Sharding by test-suite duration rather than by project count keeps the consolidated jobs balanced.
The second lever is the first-step warm up. Shallow checkouts, a packages.lock.json key for actions/cache, and dotnet build --no-restore after a restore step all shrink phase three without touching phases one and two.
What the minutes cost
Per-minute rates decide what the startup minutes are worth. warp-windows-latest-x64-8x (8 vCPU, 32 GB) costs $0.032 per minute against $0.042 per minute for the 8-core Windows larger runner (8 vCPU, 32 GB): 24 percent lower list price. GitHub list price checked on 2026-08-13, from the GitHub Actions minute multipliers reference with shapes from the GitHub-hosted runners reference.
Every Windows rate is on the pricing page.
Related Questions
Does a bigger Windows runner start faster?
No. The boot path is the same on every Windows shape, so moving from 4 vCPU to 16 vCPU shortens the work a job does and leaves the fixed startup cost where it was. Size the runner from CPU and memory percentiles in the Jobs report, and pick the label from the Windows runner catalog.
Can snapshot runners cut Windows start time?
No. Snapshot runners are supported only on WarpBuild Cloud Ubuntu runners, and snapshot labels on a Windows runner are silently ignored, so the job runs normally without them. On Windows the levers are job count and first-step work, which the guide to GitHub Actions cold starts covers across platforms.
How do I tell whether a Windows job is slow to start or only queued?
Read the Queue Timings report and the Jobs report for the same 30 days. Queue Timings gives queue time P75 and P90 per runner label and stack, and Jobs gives duration P75 and P90 for the job. A high queue time with flat duration is a capacity problem, covered in the guide to reducing GitHub Actions queue times. A flat queue time with a long duration on a one-step probe job is startup cost.
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.