Why Is My GitHub Actions Bill So High?

A high GitHub Actions bill usually comes from four drivers: oversized runners, macOS minutes, re-run storms, and cache misses that force full rebuilds.

Last verified:

Answer

A GitHub Actions bill climbs for four reasons, and they usually arrive in this order: oversized runners that bill a higher rate for vCPU the job never uses, macOS minutes priced well above Linux minutes, re-run storms from duplicate triggers and retries, and cache misses that force full rebuilds on every run. Runner cost is billed minutes multiplied by the rate of the label the job holds, so every one of those four shows up as either more minutes or a higher rate per minute.

The rate half is easy to read off a price list. On WarpBuild, the Linux x64 rate doubles at every size step.

WarpBuild runnerShapeWarpBuild rateNearest GitHub-hosted runnerGitHub rateDifference
warp-ubuntu-latest-x64-2x2 vCPU, 8 GB$0.004/minubuntu-latest on private repositories$0.006/min33 percent lower list price
warp-ubuntu-latest-x64-4x4 vCPU, 16 GB$0.008/min4-core Linux larger runner$0.012/min33 percent lower list price
warp-ubuntu-latest-x64-8x8 vCPU, 32 GB$0.016/min8-core Linux larger runner$0.022/min27 percent lower list price
warp-ubuntu-latest-x64-16x16 vCPU, 64 GB$0.032/min16-core Linux larger runner$0.042/min24 percent lower list price
warp-ubuntu-latest-x64-32x32 vCPU, 128 GB$0.064/min32-core Linux larger runner$0.082/min22 percent lower list price

WarpBuild rates come from the cloud runners documentation and the WarpBuild pricing page. GitHub list prices were checked on 2026-08-13 against the GitHub Actions per-minute rate list. One caveat before you compare invoices: GitHub gives public repositories a 4 vCPU, 16 GB Linux shape at no charge, so every GitHub number here is the private-repository price that paying teams see.

The table above compares one workload at fixed duration. Substitute measured job times for your fleet and use the WarpBuild pricing page for the full rate list.

The structure of the bill matters as much as the rates. Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee, so headcount and repository count never appear as line items. Signup includes $10 free credits, which is enough to run the diagnosis below against a real workload before you commit anything.

Detail

Start with the reports

Every fix below depends on knowing which line is large. The reports documentation covers three sections that answer the question directly, and each one exports to CSV.

QuestionWhere to lookWhat to read
Which jobs cost the most?Billing, CI tabOne row per job execution: repository, job name, runner label, stack, execution time, billed time, and the split between runner cost and snapshot cost
Which repository or label owns the growth?Billing, CI tab daily chartStacked daily cost grouped by repository or by runner label
Is a job paying for vCPU it never uses?JobsDuration P75 and P90, queue time P75 and P90, CPU P75 and P90, memory P75 and P90 per unique job
Is cache spend storage or operations?Billing, Cache tabSummary cards split total cost into storage cost and operations cost, with rows filtered by storage, operation-hit, and operation-commit
What do Docker builds cost?Billing, Docker Builder tabOne row per builder session with profile, architecture, duration, and cost
Are jobs waiting instead of running?Queue TimingsQueue time P75 and P90 per runner label and stack

Two structural facts save a lot of confusion here. Docker builder sessions bill separately from runners and appear on their own tab, so a workflow whose image build moved to a remote builder shows a smaller CI line and a new Docker Builder line. Snapshot usage bills separately inside the CI row, at $0.04 per snapshot restore and $0.025 per snapshot-hour of storage, which is why each row carries a runner cost and a snapshot cost rather than one number.

CPU and memory percentiles need CI observability enabled on the runner. Jobs without telemetry show a dash in those columns, and a dash is the reason most oversizing goes unnoticed for months.

Driver 1: oversized runners

The rate doubles at every step of the size ladder, so a job pinned to a 32 vCPU label pays eight times the per-minute rate of the same job on 4 vCPU. That trade only pays back when the extra cores actually cut wall-clock time.

Here is one job converted into a monthly number. Assume an integration suite that runs on every pull request, 300 runs a month, 14 minutes per run on warp-ubuntu-latest-x64-32x.

  • Before: 300 runs times 14 minutes is 4,200 minutes, at $0.064 per minute, which is $268.80 a month.
  • The Jobs report shows CPU P75 at 21 percent and memory P75 at 34 percent, so the suite is bound by network and disk waits rather than cores.
  • After: the same suite on warp-ubuntu-latest-x64-8x takes 17 minutes, which is 5,100 minutes at $0.016 per minute, or $81.60 a month.
  • Monthly difference: $187.20, with three minutes added to each run.

The same assumption set on the nearest GitHub-hosted shapes costs 4,200 minutes at $0.082 per minute, or $344.40, before the move, and 5,100 minutes at $0.022 per minute, or $112.20, after it. GitHub list prices checked on 2026-08-13 at docs.github.com.

Run that arithmetic per job rather than per fleet. A compile step at 90 percent CPU on 16 vCPU deserves its label; a Terraform plan at 8 percent CPU on the same label is paying quadruple for a job that waits on an API.

Driver 2: macOS minutes

macOS is the most expensive platform in any GitHub Actions fleet, and it usually dominates the invoice for iOS teams even when it is a small share of total minutes. A WarpBuild macOS runner with 6 vCPU and 22 GB costs $0.08 per minute, and the 12 vCPU, 44 GB runner costs $0.16 per minute. At those rates, 2,000 macOS minutes cost $160.00, the same as 40,000 minutes on warp-ubuntu-latest-x64-2x at $0.004.

That ratio is the reason macOS deserves its own review pass. Group the daily cost chart by runner label and the macOS line usually stands alone. Two questions then decide the fix. First, does every job on the label need macOS at all, or are packaging, linting, and metadata steps riding along because they were written into the same job? Those steps move to Linux, where warp-ubuntu-latest-x64-4x bills $0.008 per minute against $0.08 for the 6 vCPU macOS runner. Second, does the Xcode build finish in less than half the time on the 12 vCPU runner? If it does not, the 6 vCPU label costs less per run at $0.08 per minute.

A worked case: an Xcode build holding warp-macos-latest-arm64-12x for 900 minutes a month costs $144.00. The same build on the 6 vCPU runner takes 1,150 minutes and costs $92.00, a $52.00 monthly difference for a slower but cheaper run of a job nobody waits on.

Driver 3: re-run storms

Minutes multiply faster than rates do. A matrix of 10 jobs at 6 minutes each on warp-ubuntu-latest-x64-4x bills 60 minutes per run, or $0.48 at $0.008 per minute. Three common patterns then multiply that number:

  • push and pull_request both firing on the same branch, which doubles every commit to $0.96.
  • No concurrency group, so five commits pushed in ten minutes leave five full matrices running instead of one.
  • Retries on flaky jobs, where two extra attempts of a 6 minute job add $0.096 to the affected run.

At 600 runs a month the duplicated version costs $288.00 and the deduplicated version costs $144.00, on the same test code. This workflow shape removes the duplication and sizes each job separately:

name: ci
on:
  pull_request:
    branches: [main]

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

jobs:
  unit:
    runs-on: warp-ubuntu-latest-x64-4x
    steps:
      - uses: actions/checkout@v4
      - uses: WarpBuilds/setup-node@v6
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm test

  integration:
    runs-on: warp-ubuntu-latest-x64-8x
    steps:
      - uses: actions/checkout@v4
      - uses: WarpBuilds/cache@v1
        with:
          path: |
            ~/.cache/build
          key: build-${{ hashFiles('**/bun.lockb') }}
          restore-keys: build-
      - run: ./scripts/integration-tests.sh

  packaging:
    runs-on: warp-ubuntu-latest-arm64-4x
    steps:
      - uses: actions/checkout@v4
      - run: docker build -t app:arm64 .

The packaging job on warp-ubuntu-latest-arm64-4x at $0.006 per minute also shows the cheapest structural move available on Linux: any job with no x86 dependency costs less on ARM64 at every size.

Driver 4: cache misses forcing full rebuilds

A cold cache turns a 40 second dependency restore into a multi-minute install, on every job of every run. The cost is invisible on the cache tab because it lands on the runner line instead.

Assume 8 jobs per run across 300 runs a month, so 2,400 job executions, each installing dependencies in 4.5 minutes cold against 0.6 minutes warm, on warp-ubuntu-latest-x64-4x.

  • Cold: 10,800 minutes at $0.008 per minute, which is $86.40 a month.
  • Warm: 1,440 minutes at $0.008 per minute, which is $11.52, plus the cache itself.
  • Cache line items: 60 GB of storage at $0.20 per GB-month is $12.00, and 4,800 cache operations at $0.0001 each is $0.48.
  • Net: $86.40 becomes $24.00, a $62.40 monthly difference.

That split is why the Cache tab separates storage cost from operations cost. Storage dominates when a fleet keeps large caches alive; operations dominate when thousands of small jobs each read and write. Cache storage and cache operations are included at no charge on BYOC runners, so BYOC fleets read the cache tab as a usage report rather than a cost line. The caching documentation covers the key and restore-key shapes that decide the hit rate, and the cache storage cost answer covers the storage side in detail.

The four drivers on one fleet

Combining the four worked examples above into one month:

DriverBeforeAfterChange
Oversized integration suite4,200 min at $0.064 = $268.805,100 min at $0.016 = $81.60-$187.20
macOS build on the 12 vCPU label900 min at $0.16 = $144.001,150 min at $0.08 = $92.00-$52.00
Duplicate triggers on the test matrix600 runs at $0.48 = $288.00300 runs at $0.48 = $144.00-$144.00
Cold dependency caches10,800 min at $0.008 = $86.40$11.52 of minutes plus $12.48 of cache-$62.40
Total$787.20$341.60-$445.60

Nothing in that table required a change to application or test code. It required reading four reports and editing label names and triggers in workflow files.

What is not on the bill

Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee, so a team of 40 engineers and a team of 400 pay the same rate for the same minute of the same runner. Adding a repository adds cost only when it queues jobs, and archiving one removes its minutes from the next invoice with no plan change.

The same per-minute model covers the whole fleet rather than leaving one platform on a separate billing scheme. Signup includes $10 free credits.

Why did my GitHub Actions bill go up this month?

Compare the per-job billing rows across the two periods and group the daily cost chart by runner label and by repository. The usual causes are a matrix that grew, a duplicate trigger firing every commit twice, and a job pinned to a larger runner label than its CPU percentiles justify. The cost attribution guide covers how to split those rows by team and repository so the increase has an owner.

Do larger GitHub Actions runners always cost more?

The per-minute rate doubles at every size step, so a 32 vCPU Linux x64 runner at $0.064 per minute costs eight times as much per minute as a 4 vCPU runner at $0.008. A larger runner lowers the monthly total only when it cuts wall-clock time by more than the rate step adds, which is a question the Jobs report answers with duration and CPU percentiles. The larger runners cost guide walks the arithmetic for each size class.

How do I lower a GitHub Actions bill quickly?

Remove duplicate runs first, then right-size the jobs whose CPU percentiles sit far below the runner they hold, then warm the caches that force full rebuilds. Those three moves need no change to test code and, on the fleet modeled above, account for $445.60 a month. The guide to reducing GitHub Actions costs works through each lever with configuration examples, and the rate list is on the WarpBuild pricing page.

Which report shows what each GitHub Actions job costs?

The CI tab of the billing report has one row per job execution with repository, job name, runner label, execution time, billed time, and the split between runner cost and snapshot cost. Cache spend and Docker builder spend sit on their own tabs, and every table exports to CSV. See the reports documentation for the filters and the percentile columns.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.