How to Reduce Your GitHub Actions Bill
GitHub Actions bills are driven by a few simple rules, and most overspend comes from not knowing them. This guide explains how minutes are counted and lists the changes that cut the bill without slowing your team down.
How GitHub bills Actions
From GitHub's billing docs and runner pricing page:
- Public repositories on standard runners are free. Private repositories get included minutes per month: 2,000 on Free, 3,000 on Pro and Team, 50,000 on Enterprise Cloud.
- Each job's time is rounded up to the next whole minute. A 61 second job bills as 2 minutes.
- Included minutes are consumed at a multiplier by OS: Windows uses 2 minutes per minute, macOS uses 10.
- After the included minutes, you pay per minute: Linux 2-core $0.006, Windows 2-core $0.010, macOS 3-core (M1) $0.062.
- Larger runners are never covered by included minutes. Linux 4-core is $0.012, 8-core $0.022, 16-core $0.042 per minute.
- Artifact and cache storage count against separate quotas (the repository cache default is 10 GB).
Run your own numbers in the Actions cost calculator before choosing what to fix.
1. Find the expensive workflows
In your organization's billing page, open the Actions usage report and group by workflow and by runner SKU. Usually two or three workflows account for most of the spend, and macOS or large runners are far over their share. Fix those first.
2. Cancel redundant runs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
On busy PRs this alone often removes 10 to 30 percent of minutes, since each push no longer leaves an older run going. Do not use cancel-in-progress for deploy workflows.
3. Skip runs that cannot fail
Use paths or paths-ignore so docs-only and config-only changes do not trigger heavy workflows. Draft PRs can skip expensive jobs:
jobs:
e2e:
if: github.event.pull_request.draft == false
4. Set timeouts
The default job timeout is 360 minutes. One hung job at 6 hours costs $2.16 on a 2-core Linux runner, and more on anything else. Set timeout-minutes per job to about twice normal duration.
5. Cut the matrix
A matrix of 3 OSes and 4 language versions is 12 jobs, and Windows and macOS minutes cost more. Test the full matrix on main or nightly, and a single combination on PRs. See matrix builds done right.
6. Be careful with macOS and Windows
A macOS minute costs about 10 times a Linux minute. Run Linux jobs on Linux, and only the steps that need a Mac on a Mac. For iOS, build and test on macOS but run linting, API tests and docs on Linux. See macOS runners for iOS CI.
7. Make jobs shorter, not just fewer
Billing is per minute, so a faster job is a cheaper job. Caching dependencies, Docker layers and build output cuts minutes directly. Start with how to speed up GitHub Actions and the caching guide.
Watch the rounding: a job that runs 20 seconds still bills a full minute. Merge tiny jobs (for example, separate lint and format jobs) so setup and rounding are paid once.
8. Right-size the runner
A bigger runner costs more per minute but can cost less per run. The break-even is simple: a 4-core runner at $0.012 beats 2-core at $0.006 if the job finishes in less than half the time. Measure with one workflow:
| Runner | Per minute | Run time | Cost per run |
|---|---|---|---|
| 2-core Linux | $0.006 | 12 min | $0.072 |
| 4-core Linux | $0.012 | 6 min | $0.072 |
| 8-core Linux | $0.022 | 3.5 min, rounded to 4 | $0.088 |
These times are illustrative; use your own. Note how rounding and diminishing returns make the third row worse. Single-threaded steps will not shrink with more cores.
9. Trim artifacts and logs
Artifact storage is billed in GB-hours beyond the plan's included storage. Set short retention, and upload only what someone will use:
- uses: actions/upload-artifact@v4
with:
name: test-report
path: reports/
retention-days: 7
The default retention is 90 days unless your repository or organization sets a lower value. Compress large artifacts and skip uploading build output nobody downloads.
10. Consider other runners
For heavy use, running on your own machines or on a third-party runner provider can cost less per minute than GitHub's larger runners. The trade-offs are covered in self-hosted versus GitHub-hosted runners. Whatever you choose, compare cost per workflow run, not the price per minute.
Set budgets
In GitHub billing settings, create a budget for Actions with a stop-usage option or alerts. A runaway workflow, such as a cron job on a large matrix, will then hit a cap before it hits your credit card. Use the CI minutes forecaster to set the budget from expected usage, and read forecasting CI costs to plan for team growth.
FAQ
Are failed runs billed?
Yes. Any minutes a job runs are billed, whether it passes or fails, so fixing flaky failures and reruns saves money. See dealing with flaky tests.
Do waiting jobs cost money?
Queue time is not billed. A job waiting on an approval or a concurrency group is not running.
Do included minutes apply to larger runners?
No. Larger runners are always billed at the per-minute rate.
Is Windows worth avoiding?
Only run on Windows what must run there. A Windows 2-core minute is $0.010 versus $0.006 for Linux, and uses included minutes at double rate.
Check your own jobs
If you want a second opinion on a specific workflow, compiler.dev's comparison mode runs it on faster runners and shows run time and cost next to your GitHub-hosted run. Pricing for compiler.dev is published in the pricing section; compare cost per run, not just the per-minute rate.
Made by compiler.dev. Free tools · Pricing