GitHub's pricing page lists $0.006/minute for standard Linux. That feels cheap until you do the math. A 20-person team running 300 jobs a day at 6 minutes each hits $238/month on Linux alone -- and that's before anyone touches macOS or a large runner. This post breaks down what you're actually paying at different team sizes using the post-January 2026 rates, shows where the real cost comes from, and covers the three approaches teams use to reduce that bill.
The baseline: GitHub-hosted runner pricing (2026)
GitHub cut hosted runner prices by 25-40% effective January 1, 2026. Here are the current rates.
Standard runners (2-core):
| Runner | Rate/min | Rate/hour |
|---|---|---|
| Linux x64 (standard) | $0.006 | $0.36 |
| Linux arm64 (standard) | $0.005 | $0.30 |
| macOS standard (3-core M1) | $0.062 | $3.72 |
| Windows x64 (standard) | $0.010 | $0.60 |
Large Linux runners (x64):
| Cores | Rate/min |
|---|---|
| 4-core | $0.012 |
| 8-core | $0.022 |
| 16-core | $0.042 |
| 32-core | $0.082 |
| 64-core | $0.162 |
A few billing mechanics that matter:
- Per-minute, rounded up. A job that finishes in 1 minute 1 second is billed as 2 minutes. This compounds when you run many short jobs.
- Large runners (4-core+) are never covered by included minutes. Every minute on a 4-core or larger runner is billed at the per-minute rate regardless of your plan.
- Included minutes only apply to standard 2-core runners in private repos. Public repos pay nothing; private repos on the free plan get 2,000 minutes/month.
Free included minutes per plan (standard runners, private repos only):
| Plan | Included minutes/month |
|---|---|
| Free (personal/org) | 2,000 |
| Pro | 3,000 |
| Team | 3,000 |
| Enterprise Cloud | 50,000 |
Once you exceed the included minutes, every additional minute is billed at the standard rate. Enterprise Cloud's 50,000 minutes sounds like a lot -- at 300 jobs/day averaging 6 minutes each, a 20-person team burns through roughly 54,000 minutes/month, putting them over the Enterprise limit after about 27 days.
What the bill looks like at different scales
| Team size | Jobs/day | Avg job time | Monthly cost (Linux std) | macOS equivalent |
|---|---|---|---|---|
| 5-person | 50 | 4 min | ~$26 | ~$269 |
| 20-person | 300 | 6 min | ~$238 | ~$2,470 |
| 50-person | 1,000 | 8 min | ~$792 | ~$8,215 |
These are Linux-only numbers using 30-day months. The macOS column shows what those same job volumes cost if you run on macOS runners -- a 10x multiplier over standard Linux. For teams building iOS or macOS apps, the "standard" column is not the relevant one.
Where the real cost comes from
Knowing the per-minute rate is only half the picture. The bill is shaped by how workflows are structured.
Matrix builds are the largest multiplier most teams don't track. A matrix of 4 operating systems x 3 Node.js versions generates 12 concurrent jobs per PR. If each takes 5 minutes, one PR merge costs 60 minutes of compute. With 20 engineers each opening 3 PRs a week, that's 3,600 minutes/week from matrix alone -- $21.60/week, or roughly $93/month, from a single workflow.
Large runners amplify costs further. Switching from a 2-core ($0.006/min) to an 8-core ($0.022/min) for a memory-intensive build is a 3.7x rate increase. Going to 16-core ($0.042/min) is 7x the base rate. Teams sometimes over-provision runner size as the default fix for slow builds, without profiling where the bottleneck actually is.
macOS builds are the most expensive category. At $0.062/min, a 10-minute iOS build costs $0.62 -- more than a full hour of Linux standard compute. Teams running hundreds of iOS builds per day can hit $4,000-$8,000/month on macOS runners alone.
Unoptimized integration tests run on every push even when the changed files have no connection to the test scope. Without conditional execution (paths: filters or manual job conditions), a documentation change triggers the same 15-minute test suite as a database migration.
Multiple workflows triggering per push stacks costs. If lint, CI, and deploy all fire on the same push event with no dependency between them, three workflows run in parallel when one sequential pipeline might have been enough.
The three approaches teams take
Option 1: Optimize the workflows
The cheapest option is reducing how many minutes you consume on GitHub's infrastructure. Common techniques:
- Cache dependencies using
actions/cache. npm and pip installs that take 2-3 minutes on each job run are often fully cacheable. - Conditional job execution: only run expensive jobs on
mainor release branches. PRs get a limited subset. Useif: github.ref == 'refs/heads/main'or path filters. - Limit matrix size in PRs: run the full matrix on merge, but run only the base OS during development.
Result: typically 20-40% reduction in monthly spend. This approach works well at smaller team sizes and gives diminishing returns as the codebase and test suite grow. It also adds maintenance overhead -- engineers need to understand the conditional logic and update it as the repo evolves.
Option 2: Switch to self-hosted runners
Install Actions Runner Controller (ARC) on your own Kubernetes cluster and replace ubuntu-latest with your own runner labels. The compute cost is now whatever you pay for the underlying servers -- which may already be provisioned for other workloads.
The tradeoff is operational. You manage fleet scaling, node provisioning, image updates, availability, and incident response. If a runner node goes down during a deployment pipeline, that's your problem to resolve, not GitHub's. For teams without existing Kubernetes infrastructure, standing this up is a non-trivial investment.
Result: near-zero runner spend if the machines are already paid for. Adds ongoing operational overhead.
Option 3: Hybrid routing (self-hosted first, cloud as overflow)
Hybrid routing combines the cost savings of self-hosted with the reliability floor of GitHub-hosted. Jobs route to self-hosted runners when the fleet has capacity and fall back to GitHub-hosted runners when it's full. No workflow file changes required -- the routing happens at the infrastructure layer.
Result: the same cost savings as Option 2 during normal load, with a guaranteed fallback so builds don't queue when your fleet is saturated.
macOS builds: the case for self-hosted Mac hardware
The macOS runner economics are different from Linux in one important way: Apple Silicon hardware is commodity now.
A Mac Mini M4 costs $599. GitHub's macOS runner costs $0.062/min.
Break-even calculation:
- $599 / $0.062/min = 9,661 minutes of macOS CI time to break even
- At 300 macOS CI minutes/day: break-even in ~32 days
- At 100 macOS CI minutes/day: break-even in ~97 days
Most teams building iOS apps consistently exceed 100 minutes/day. At 300 minutes/day, the hardware pays for itself in a month and then runs essentially free.
Real-world data point: Jeff Verkoeyen (writing in October 2025) cut his macOS CI bill from approximately $4,167/month to approximately $68/month by running two M4 Mac Minis. The hardware paid back in under 5 months. The $68/month remaining covers occasional GitHub-hosted overflow when the local machines were at capacity.
The 2026 context: pricing went both ways
Two things happened to the self-hosting math in early 2026, in opposite directions.
GitHub cut hosted runner prices by 25% for Linux on January 1, 2026. Linux went from $0.008/min to $0.006/min. macOS went from $0.080/min to $0.062/min. Windows went from $0.016/min to $0.010/min. The per-minute savings from self-hosting are slightly smaller than they were in 2025, but the directional math is unchanged for high-volume teams.
GitHub proposed a $0.002/min fee for self-hosted runners in private repos in March 2026, then postponed it after user backlash. The fee is not currently in effect, but it's described as pending re-evaluation rather than withdrawn. If enacted, a team running 123,200 self-hosted minutes/month would see an additional $246/month in platform fees on top of their own compute costs. That would reduce but not eliminate the savings from self-hosting at high volumes.
The self-hosting economics still favor teams with significant job volume. The margin narrowed in 2026, but the direction didn't change.
If your GitHub Actions bill is something you're actively tracking, Stratus routes your workflows to self-hosted runners first and falls back to GitHub-hosted only when your fleet is full -- no workflow changes required.
Join the waitlist →