GitHub Actions Cost Report 2026
Where your Actions spend actually goes, how a small slice of premium-OS and matrix minutes drives the majority of it, and how much is recoverable without rewriting a single workflow.
Executive summary
For most teams the GitHub Actions bill is not one number, it is a stack of small decisions: which runner operating system a job lands on, how wide a matrix fans out, whether dependencies are cached, and how often a flaky job gets re-run. Each looks cheap in isolation, and together they are usually the largest variable line item in an engineering organization's compute budget. The bill does not have a single cause, which is why a single cut rarely fixes it; it has a shape, and the shape is what this report maps.
This report decomposes a representative monthly Actions bill into its real drivers. The headline finding is consistent across team sizes: a small slice of premium-OS and matrix minutes drives the majority of cost, while a separate slice of uncached and re-run minutes is pure waste. The two are different problems with different fixes. The first is a placement problem, where work is running on a more expensive operating system than it needs. The second is a runner problem, where minutes are being spent recomputing or retrying things that did not need to be recomputed or retried.
The premium-OS effect is the one teams most consistently underestimate, because the multipliers are large and the matrix hides them. A macOS minute costs about 10 times a Linux minute and a Windows minute about 2 times, so a cross-platform matrix that looks balanced by job count is wildly unbalanced by cost. A handful of macOS and Windows legs, often running lint and unit tests that would pass identically on Linux, can quietly account for more than half the invoice while serving a minority of the jobs.
The waste side is more mundane but just as large. Uncached dependency installs reinstall the same packages on every run, and flaky re-runs spend minutes recomputing results that were already correct. Together they account for roughly a third of billed minutes on a typical mid-size pipeline, and neither bought anything. A warm dependency cache eliminates the repeated installs, and automated recovery of transient failures eliminates most of the re-run minutes, both without changing what the pipeline tests.
The recoverable portion is large and does not require rewriting pipelines, which is the part that surprises teams bracing for a migration project. Moving the heavy build and test legs to a managed Linux runner layer captures roughly 69% of per-minute cost, and because only the runner label changes, the workflow YAML stays put. The full report quantifies each lever and orders them by payback, so a team can start with the change that moves the bill the most for the least effort.
Where a typical cross-platform team's Actions dollars land, by runner operating system. · Source: GitHub Actions pricing + Latchkey analysis (modeled)
Published GitHub-hosted per-minute rates against a managed Linux runner. · Source: GitHub Actions pricing + Latchkey rates
Email me the report
The full report is right here on this page, free. Want the link in your inbox to read later or share, plus new Latchkey reports as they drop? Drop your email and we will send it over.
Sent! Check your inbox for the report link.
No spam. Unsubscribe anytime.
A third of the bill is premium-OS minutes you may not need
macOS minutes cost about 10 times Linux and Windows about 2 times, so cross-platform teams routinely find that macOS and Windows together drive over half the bill while serving a minority of jobs. The chart of the bill by operating system makes this concrete: the macOS slice is large out of all proportion to how many jobs run there, because each of those minutes costs so much more than a Linux minute.
The reason this happens is almost always the same when teams audit their pipelines. The premium-OS legs are frequently running work that did not need that operating system at all: linting, unit tests, and dependency resolution that would pass identically on Linux. The expensive runners end up doing cheap, OS-agnostic work, which is the worst possible allocation, paying the highest per-minute rate for the work least dependent on the platform.
The fix is to push the heavy, OS-agnostic work onto Linux and reserve macOS and Windows for the genuinely platform-specific legs: signing, packaging, and platform UI tests. The platform-specific surface is almost always much smaller than the matrix that grew up around it, so this single reshaping often cuts a cross-platform bill by a third or more with no loss of coverage. It is usually the single largest cost lever available, and it requires only moving where jobs run, not changing what they do.
- macOS at ~10x and Windows at ~2x Linux mean a balanced job count is a wildly unbalanced cost.
- Premium-OS legs often run lint and unit tests that would pass identically on Linux.
- Reserving macOS and Windows for signing, packaging, and platform UI tests cuts the bill without losing coverage.
Matrix jobs are the fastest way to multiply spend
A matrix is convenient and silently multiplicative: three language versions across three operating systems is nine jobs from one workflow file. The cost curve as a workflow fans out is steeper than engineers expect, because each added dimension stacks on the premium-OS multiplier rather than adding to it. A single Linux job is the baseline, three Node versions triple it, adding a macOS leg multiplies it again, and adding a Windows leg pushes it to roughly eleven times the original.
The multiplicative shape is the trap. Engineers reason about matrices additively, as if each new axis adds a fixed cost, but the axes multiply each other and then the premium-OS rate multiplies the result. A matrix that looks like a reasonable coverage decision in the YAML becomes a double-digit multiplier on the bill, and because the cost is spread across many small jobs, no single job looks expensive enough to notice.
Trimming low-value matrix cells and reserving the full grid for release branches recovers cost without reducing real coverage. Most pull requests do not need the full cross-product of versions and operating systems; they need enough coverage to catch regressions, with the exhaustive grid reserved for the release branch where it earns its cost. This keeps the multiplier from applying to every push while preserving the thorough validation where it actually matters.
- Matrix axes multiply each other, then the premium-OS rate multiplies the result.
- The cost spreads across many small jobs, so no single job looks expensive enough to flag.
- Reserving the full grid for release branches keeps the multiplier off every push.
Relative monthly cost as a single workflow fans out across OS and version dimensions. · Source: Latchkey analysis (modeled)
Roughly 31% of minutes are recoverable waste, not work
Uncached dependency installs and flaky re-runs together account for nearly a third of billed minutes on a typical mid-size pipeline. The split of billed minutes shows useful build and test work as the majority, but a substantial remainder goes to uncached installs, flaky re-runs, and queue plus cold-start time. None of that remainder produced a result the team needed; it is the mechanical overhead of running the pipeline badly.
Both of the large waste categories are mechanical and addressable. A warm dependency cache eliminates the repeated installs, because the packages that did not change since the last run are restored rather than reinstalled. Most failed jobs are transient, registry timeouts, network blips, out-of-memory kills, and pass on a clean retry, so self-healing runners reclaim the re-run minutes without any change to test code. The waste is real but it is not fundamental, which means it can be removed without touching the application.
The framing that helps here is to separate useful minutes from recoverable minutes and track them as distinct numbers. A team that knows roughly a third of its minutes are recoverable can set a target for that share and watch it come down as caching and self-healing land, rather than treating the whole bill as an undifferentiated cost. The recoverable third is the cheapest part of the bill to attack, because removing it requires configuration rather than re-architecture.
Estimated split of a mid-size team's billed minutes into useful work and recoverable waste. · Source: Latchkey analysis (modeled)
The managed-runner saving is about 69%, with no workflow rewrite
Per-minute, a managed Linux runner lands near $0.0025 against a GitHub-hosted Linux rate of $0.008, before counting the premium-OS minutes it displaces. The per-minute chart shows the gap directly: the managed bar sits well below the hosted Linux bar, and far below the Windows and macOS bars, so the saving compounds when managed Linux runners absorb work that was running on a premium operating system.
The reason this is capturable without a migration project is that only the runner label moves. The workflow YAML is unchanged, the steps are unchanged, the tests are unchanged; the job simply runs on a managed runner instead of a hosted one. That is a one-line change per workflow rather than a re-architecture, which is why managed runners are the default cost lever for teams without a dedicated platform group. The risk is low because nothing about what the pipeline does changes, only where and at what rate it runs.
The saving stacks with the placement and waste fixes rather than overlapping them. Moving premium-OS work to Linux reduces the expensive minutes, caching and self-healing remove the wasted minutes, and the managed rate then prices the surviving useful minutes cheaply. A team that does all three captures the product of the three savings, which is why the recoverable portion of the bill is so much larger than any single lever suggests.
Idle and queue time is the hidden tax of self-hosting
Self-hosted runners look cheaper per-minute until idle compute, autoscaling lag, and the engineering time to patch and clean up the fleet are priced in. The spreadsheet comparison of a raw instance price against a hosted per-minute rate omits the two costs that actually decide the question, and both of them favor a managed layer once they are included.
Idle compute is the gap between capacity and demand. CI load is spiky, so a self-hosted fleet sized for peak sits mostly idle off-peak, and a fleet sized for the average starves jobs during peak, which shows up as queue time on the billed-minute split. Either way the team pays, in money for idle instances or in developer wait time for queued jobs, and neither cost appears in the naive instance-price comparison.
A managed layer captures most of the compute saving while charging only for minutes actually run, which is why the break-even math increasingly favors managed over a hand-rolled fleet. The engineering time to patch images, scale the fleet, harden security, and clean up orphaned runners is continuous work that competes directly with shipping product, and a managed layer removes it entirely. The relevant comparison is total cost of ownership, including that team time, not instance price against hosted price.
Caching is the cheapest minute you will ever remove
Of the recoverable categories, uncached dependency installs are the easiest to eliminate and among the largest, which makes caching the highest ratio of payoff to effort on the whole bill. A cache scoped to the lockfile and toolchain version restores unchanged dependencies in seconds instead of reinstalling them in minutes, and the saved minutes show up directly on the next invoice.
The reason so many teams do this badly or not at all is that caching is conceptually simple but operationally finicky. A cache keyed too loosely serves stale content and causes subtle failures; a cache keyed too tightly almost never hits and saves nothing. The teams that win treat the cache key as a first-class part of the pipeline and measure the hit rate the way they measure test coverage, because a cache nobody measures is a cache that silently stops working.
Caching pairs naturally with the managed runner layer, because the cache needs somewhere durable to live across ephemeral jobs. A managed runner that holds the cache between jobs keeps the hit rate high without the team hand-wiring remote cache storage in every repository, so the cheapest minute to remove gets removed automatically rather than depending on per-repository configuration discipline that erodes over time.
The bill has a shape, so order the fixes by payback
Because the Actions bill is a stack of small decisions rather than a single cost, the right approach is to attack it in order of payback rather than all at once. The placement fix, moving premium-OS work to Linux, is usually the largest single lever and should come first. The matrix trim is next, removing the multiplier from every push. Caching and self-healing remove the recoverable waste, and the managed rate prices the survivors cheaply.
Ordering matters because the levers interact. Trimming the matrix before moving work to Linux leaves expensive macOS and Windows legs in the trimmed grid; moving to Linux first means the trim applies to cheaper minutes. Similarly, caching before adopting a managed runner leaves the cache homeless across ephemeral jobs; pairing them captures the hit rate. A team that sequences the fixes thoughtfully captures more than a team that applies them in a random order.
The encouraging conclusion is that none of these levers is a moonshot. Placement, matrix discipline, caching, self-healing, and a managed runner rate are pipeline hygiene, not a platform-team project. A team can work down the ordered list incrementally, measuring the bill after each change, and watch a cost that felt fixed turn out to be largely recoverable, most of it without touching a line of application code.
Recommendations
Move OS-agnostic work to Linux and reserve premium OSes for what needs them
This is usually the single largest lever. Audit which jobs genuinely require macOS or Windows and move lint, unit tests, and dependency resolution to Linux, reserving the premium operating systems for signing, packaging, and platform UI tests. It often cuts a cross-platform bill by a third with no loss of coverage.
Trim low-value matrix cells and reserve the full grid for release branches
Matrix axes multiply each other and then the premium-OS rate multiplies the result, so the full cross-product on every push is expensive coverage. Run enough of the grid on pull requests to catch regressions and reserve the exhaustive matrix for the release branch where it earns its cost.
Cache dependencies durably and measure the hit rate
Uncached installs are the cheapest waste to remove. Scope cache keys to lockfiles and toolchain versions, give the cache somewhere durable to live across ephemeral jobs, and monitor the hit rate the way you monitor test coverage so it does not silently stop working.
Auto-heal transient failures instead of re-running them by hand
Most failed jobs are transient and pass on a clean retry. Self-healing runners reclaim the re-run minutes without touching test code and keep flaky red checks from reaching a developer, removing a meaningful slice of the recoverable third of the bill.
Adopt managed runners for the per-minute saving without a migration
A managed Linux runner near $0.0025 against the $0.008 hosted Linux rate captures roughly 69% per minute, and because only the runner label changes the workflow stays put. This is the default cost lever for teams without a dedicated platform group.
Outlook
Expect the gap between teams that treat the Actions bill as a shape and teams that treat it as a single number to widen. The levers in this report compound: moving work to Linux, trimming the matrix, caching, self-healing, and a managed rate each multiply the others, so a team that adopts all of them does not merely spend less, it spends a fraction. Teams that leave the bill undifferentiated keep paying for premium-OS minutes they do not need and recomputing work that did not change, and the cost grows with their codebase rather than with their changes.
The structural direction is toward the runner layer absorbing the optimizations that teams currently hand-wire per repository. Durable caching, automated recovery, right-sizing, and managed per-minute economics are converging into an expected baseline, which moves the cost-control burden off individual pipeline authors and onto the platform underneath them. As that baseline settles, an undifferentiated, premium-OS-heavy, uncached bill will read less as normal and more as a sign of a pipeline nobody has tuned.
For most teams the practical takeaway is that the Actions bill is far more recoverable than it feels, and recovering it does not require a heroic project. It requires understanding the shape of the bill, attacking the levers in order of payback, and putting a runner layer underneath that removes the mechanical waste automatically. The organizations that internalize that will spend the next two years treating CI cost as a managed line item while their peers keep paying a tax they could have removed with configuration changes.
Methodology
This report combines published GitHub-hosted runner pricing with Latchkey's analysis of aggregate CI runner economics across representative pipeline shapes. Figures labeled "modeled" are illustrative estimates derived from public pricing and typical job mixes, not a primary survey, and are intended to show direction and magnitude rather than a precise population value; pricing reflects the published GitHub-hosted Linux, Windows, and macOS rates and the Latchkey managed rate. Cost shares assume a cross-platform mid-size team and will vary with each org's OS mix and matrix breadth. Figures labeled "modeled" are illustrative estimates derived from public pricing and typical pipeline shapes, not a primary survey; figures attributed to a named source reflect that source. Pricing reflects published rates at time of writing and should be verified against current provider pricing.
Sources
- GitHub Actions - billing & pricing
- GitHub - Octoverse
- Stack Overflow Developer Survey
- DORA State of DevOps Report