Can I Set a Spending Limit on GitHub Actions Runners?

A spending limit on GitHub Actions runners lives in the workflow file: runner labels, timeout-minutes, concurrency groups, and path filters cap billed minutes.

A spending limit on GitHub Actions runners is set in the workflow file rather than on the invoice, and the four controls that put a real ceiling on the bill are the runner label, timeout-minutes, the concurrency group, and path filters. Each one caps minutes before they are spent, and each one is a few lines of YAML you can merge today.

Answer

A budget or spending limit configured in GitHub billing settings applies to the products GitHub meters. Jobs that run on self-hosted or third-party runner labels are not billed by GitHub at all, so they never draw that limit down (GitHub Actions billing documentation, checked on 2026-08-13). A number entered in billing settings therefore does nothing to a job already running on a warp- label.

On the WarpBuild side the bill is billed minutes multiplied by the rate of the label the job holds. Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee, so the only two terms available to bound are how many minutes run and what each minute costs. All four controls below act on one of those two terms.

ControlWhich term it capsCeiling it setsWhere it goes
Runner labelThe rate of every billed minuteFixed by the label, from $0.003 to $0.128 per minuteruns-on
timeout-minutesThe minutes one job can billThe value you set; 360 minutes if you set nothingJob level, or per step
concurrency with cancel-in-progressThe minutes burned by runs a newer push has supersededOne run per group in flightWorkflow or job level
paths filtersHow many runs start at allRuns only on commits touching the listed pathson.push and on.pull_request

The 360 minute default and the concurrency behavior come from the workflow syntax reference, checked on 2026-08-13. A paths filter is evaluated before the run is created, so a filtered out workflow produces no run and no billable job (workflow syntax, checked on 2026-08-13). WarpBuild rates come from the pricing page, also checked on 2026-08-13.

Detail

The rate the label locks in

The price of a job minute is decided the moment the label is written. The last column is the worst case for a single job that hangs and rides the 360 minute default with no timeout-minutes set.

Runner labelShapeRateOne job at the 360 minute default
warp-ubuntu-latest-arm64-2x2 vCPU, 8 GB$0.003/min$1.08
warp-ubuntu-latest-x64-2x2 vCPU, 8 GB$0.004/min$1.44
warp-ubuntu-latest-arm64-4x4 vCPU, 16 GB$0.006/min$2.16
warp-ubuntu-latest-x64-4x4 vCPU, 16 GB$0.008/min$2.88
warp-windows-latest-x64-4x4 vCPU, 16 GB$0.016/min$5.76
warp-macos-latest-arm64-6x6 vCPU, 22 GB$0.08/min$28.80
warp-windows-latest-x64-32x32 vCPU, 128 GB$0.128/min$46.08

Rates from the pricing page, checked on 2026-08-13. A test suite pinned to the 4 vCPU x64 label that would finish in the same wall-clock time on the ARM64 shape of the same size pays $0.002 more for every minute it runs, which is the cheapest structural change on the list.

What each of the other three controls is worth

timeout-minutes bounds the worst case per job. The two hung jobs in the table above cost $2.88 and $28.80 at the default. The same two jobs with timeout-minutes: 20 and timeout-minutes: 45 bill at most $0.16 and $3.60. One line per job converts an open-ended exposure into a number you can multiply by the number of jobs in the repository.

Concurrency bounds redundancy. A pull request that receives five pushes in an afternoon starts five runs of the same 20 minute macOS job, which is 100 minutes and $8.00 at $0.08 per minute if every run finishes. With cancel-in-progress: true, the four superseded runs bill only the minutes they ran before cancellation reached them, roughly 3 minutes each, so the afternoon is 12 minutes of cancelled work at $0.96 plus $1.60 for the run that survives: $2.56 in total.

Path filters bound volume. In a monorepo doing 400 pull request runs a month where iOS code changes on 90 of them, an ungated 20 minute macOS job bills 8,000 minutes, or $640.00 at $0.08 per minute. Gating the same job on ios/** brings it to 1,800 minutes, or $144.00, and the 310 filtered runs create no job at all.

One expensive job carrying three ceilings

name: ios
on:
  pull_request:
    paths:
      - "ios/**"
      - "Package.resolved"
      - ".github/workflows/ios.yml"

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

jobs:
  xcode-build:
    runs-on: warp-macos-latest-arm64-6x
    timeout-minutes: 45
    steps:
      - uses: actions/checkout@v4
      - run: xcodebuild -scheme App -destination 'generic/platform=iOS' build

The ceiling on this job is now arithmetic you can hand to a finance team. Forty five minutes at $0.08 per minute is $3.60 for the worst run, only pull requests touching ios/** start it, and one run per branch is in flight at a time. Ten branches pushing at once bound the wave at $36.00. Without the timeout, the same worst run is $28.80.

The monitoring loop that watches the number

Ceilings bound the worst case. Daily data tells you the actual case. The Billing section of Reports carries a daily cost chart grouped by repository or by runner label, summary cards for total, runner, and snapshot cost, and a per-job table with execution time, billed time, and the cost split. Every tab exports to CSV.

Create an API key with the ci and cache scopes and authenticate with an Authorization: Bearer wkey-... header, as described in the automation documentation. Then run the check on a schedule.

name: runner-spend-guard
on:
  schedule:
    - cron: "0 13 * * *"

jobs:
  check:
    runs-on: warp-ubuntu-latest-x64-2x
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/check-runner-spend.sh
        env:
          WARPBUILD_API_KEY: ${{ secrets.WARPBUILD_API_KEY }}
          MONTHLY_BUDGET_USD: "800"
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

The guard job runs on a 2 vCPU runner at $0.004 per minute and finishes inside a minute, so 30 daily runs cost about $0.12 a month. Two alert rules cover most of what goes wrong: month-to-date total against the budget, and any single runner label whose daily cost doubles day over day. The second rule catches the matrix that grew in a merged pull request nobody costed.

What a ceiling cannot do

Ceilings cap minutes. They do not make a slow job fast, and a timeout that fires turns a cost problem into a failed run, so set the value above the p95 duration of the job rather than at its median. Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee, so nothing else on the invoice moves when headcount or repository count changes. Signup includes $10 free credits, which is enough to run the four controls above against a real repository before an invoice exists. The full rate list is on the pricing page.

Does a spending limit in GitHub billing settings cover third-party runner minutes?

No. GitHub meters Actions minutes for GitHub-hosted runners, and jobs on self-hosted or third-party labels are not billed by GitHub (GitHub Actions billing documentation, checked on 2026-08-13), so a limit set there never sees them. Budget the workflow instead, using the ceilings above and the process in the GitHub Actions minutes budget guide.

What is the default job timeout in GitHub Actions?

360 minutes, per the workflow syntax reference, checked on 2026-08-13. At that ceiling one hung job bills $2.88 on warp-ubuntu-latest-x64-4x and $28.80 on warp-macos-latest-arm64-6x at the rates on the pricing page. The job timeout definition covers step level timeouts and how cancellation interacts with billed time.

How do I get alerted before the runner bill grows?

Pull the daily cost figures from Reports, compare month-to-date spend against your budget, and post to Slack on the crossing. The runner spend forecast answer turns the same daily series into a quarterly number, and the rates behind both models are on the pricing page.

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.