5 free GitHub Actions cost patterns
These are the highest-impact patterns. If you apply only these five, a typical PR-heavy repo's monthly Actions bill drops by roughly half. No tools required to apply them - they are YAML edits.
Find which of these patterns are missing in your YAML. Price a workflow
See the dollar number before and after applying these.
1. Cancel superseded runs with concurrency
Why Without a concurrency block, a fast push-to-push-to-push streak runs CI three times for the same final state. On a busy repo with 50 pushes/day this is the single biggest leak.
Apply Top of every workflow that runs on push or pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Catch For deploy workflows you usually want cancel-in-progress: false so a deploy in progress finishes; concurrency just queues the next one.
2. Use ubuntu-latest unless you genuinely need macOS or Windows
Why macos-latest runs 10x the cost. windows-latest 2x. A test job that "happens to be" on macOS doubles or worse the bill of every PR.
Apply Audit runs-on: lines. macOS is required only when you build or sign Apple binaries (xcodebuild, codesign, notarytool). Windows is required only for native Windows code paths.
Catch Cross-platform tests should still exist. Move them to a nightly schedule or a workflow_dispatch job rather than running every PR.
3. Cache language tooling
Why Without cache:, setup-node, setup-python, setup-java, setup-go, and setup-dotnet re-download dependencies on every run. 30 to 90 seconds per job, sometimes more.
Apply
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
Catch Cache keys depend on lockfiles. If the lockfile path is non-standard, set cache-dependency-path.
4. Set timeout-minutes on every job
Why The default is 360 minutes (6 hours). A hung step burns six hours of paid runner time before GitHub kills it.
Apply On every job:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 15
Catch Tune per job. Long-running builds deserve more headroom; tests deserve less.
5. paths: filters on heavy workflows
Why A 20-minute integration suite has no business running when a PR only touched the README.
Apply
on:
pull_request:
paths:
- 'src/**'
- 'tests/**'
- 'package.json'
- 'package-lock.json'
Catch Branch-protection rules that require this workflow won't see a status if it skips. Use paths-ignore to skip the workflow entirely, or keep a "always run" stub job that posts a green status when the heavy job isn't needed.
Auto-apply patterns 1, 4 (and others)
Patterns 1 and 4 above (concurrency and timeouts) are two of the four that
ci-doctor --fix
applies for you, in place. Free, MIT, no signup.
npx ci-doctor --fix --dry-run # preview the patches
npx ci-doctor --fix # apply
Get the other 25 patterns
The full Cut Your CI Bill cookbook
30 patterns total - cost, speed, security, maintenance - plus 5 paste-ready hardened workflow templates (CI, release, security scan, nightly, deploy). All MIT-licensed templates, lifetime updates. $19, one-time.
The cookbook covers what the patterns above stop short of: matrix sizing, artifact retention strategy, draft-PR skipping, OIDC for cloud auth, reusable workflows, dependabot grouping, and the security patterns (least-privilege permissions, SHA pinning, untrusted-input handling).
Get the cookbookSee also
- CI cost benchmarks - 20 popular OSS projects, 902 real findings, modeled monthly cost.
- Blog post on the dataset - how the numbers were generated.
- Stop linting your GitHub Actions, fix them -
ci-doctor --fixdeep dive.