GitHub Actions billing has four moving parts most teams underestimate: minutes, OS multipliers, matrix expansion, and artifact storage. They compound. The bill arrives at the end of the month and nobody quite knows why it doubled.
This is the field guide. The calc that runs your numbers is at /tools/gh-actions-cost.
The four moving parts
1. Minutes
The base unit. GitHub charges by the minute of compute, rounded up. Each plan includes a free tier (2,000 minutes Free, 3,000 Team, 50,000 Enterprise). Beyond that, you pay per minute.
Standard Linux runner: $0.008 per minute. That sounds tiny. Then:
2. OS multipliers
The base $0.008 is for Linux. Other OSes are billed at a multiplier:
- Windows: 2x ($0.016/min)
- macOS: 10x ($0.08/min)
If you run mobile CI on macOS hosted runners, this is most of your bill. iOS test matrix on a hosted macOS runner: 10 minutes per job, 20 jobs per matrix expansion, 40 runs per day, 22 working days = 176,000 macOS-equivalent minutes per month. That is $14,080.
3. Matrix expansion
A single workflow with a matrix of 3 OSes x 4 Node versions x 2 architectures = 24 jobs. Each one bills separately. Teams forget this. The line item in GitHub's billing dashboard says "minutes" but the multiplier is hidden inside the matrix.
I have seen a single innocuous-looking workflow file responsible for 80 percent of an org's bill because the matrix grew quietly over a year.
4. Artifact storage
$0.008 per GB-month, beyond the 2 GB included. Most teams treat artifacts as ephemeral and never clean up. A year of build artifacts at 100 MB per build, 100 builds per day = 36 TB. That is roughly $290 per month for storage of files nobody is reading.
The audit, in five lines of math
This is what the calc does behind the scenes:
total_minutes = runs_per_day * working_days * parallel_jobs * matrix_multiplier * avg_minutes
billable_minutes = max(0, total_minutes - plan_included_minutes)
compute_cost = billable_minutes * rate(os, runner_size)
artifact_cost = max(0, artifacts_gb - 2) * 0.008
total = compute_cost + artifact_cost
Plug in your numbers. Compare to your last invoice. The delta is your story.
Where the savings come from
Savings 1: matrix pruning
Most matrices are 30 to 50 percent over-specified. You do not need to test on Node 20 if your production runs on Node 22. You do not need both x86 and ARM if you only ship one of them. Prune the matrix to the configurations your customers actually use.
Median saving in audits: 20 to 30 percent of CI minutes.
Savings 2: cached dependencies
The single highest-impact change. If your test suite reinstalls dependencies on every run (no cache), 3 to 5 minutes of every job is wasted on installs. With actions/cache configured correctly, that drops to 10 seconds.
For a team running 100 jobs per day, that is 5 hours of CI time per day, every day, gone. Annualized, that is 1,825 hours.
Savings 3: self-hosted runners for the heavy stuff
Hosted Linux at $0.008/min is fine for light loads. Heavy parallel test matrices or anything with macOS at $0.08/min becomes a self-hosted candidate.
A c6a.4xlarge EC2 instance (16 vCPU, $0.61/hour on-demand) running self-hosted runners with 4 parallel jobs costs roughly $440/month if always-on. If it replaces 55,000 hosted minutes/month, you save $0 (breakeven). If it replaces 200,000 minutes/month, you save $1,160/month.
For macOS, the math is even more lopsided. A used Mac mini M2 ($600 one-time) running self-hosted iOS CI replaces hundreds of dollars per month of hosted macOS minutes within the first 30 days.
Savings 4: artifact retention policies
Default retention is 90 days. Most teams need 7 or 14. Set a retention policy on every workflow:
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 7
Median artifact storage saving: 60 to 80 percent.
The honest tradeoffs of self-hosted
Self-hosted is not free. You pay in:
- Ops time. Someone has to maintain the runners. Updates, security patches, restart loops.
- On-call. When the runner dies at 2 AM and CI is red, that is your phone ringing.
- Security surface. Self-hosted runners that run code from public PRs are an RCE waiting to happen. Either disable forks or use ephemeral runners.
The break-even is roughly 50,000 hosted minutes per month before self-hosted starts winning, once you include realistic ops overhead.
Receipts
- 9 teams reviewed CI bills in the last 90 days.
- Median monthly Actions spend: $1,820.
- Median savings opportunity: $560 per month (30 percent).
- Most common single fix: dependency caching.
- Largest single saving: $3,400 per month (a startup with a macOS matrix nobody had pruned in 18 months).