How GitHub Actions minutes are billed

The arithmetic a GitHub Actions cost estimate is built from. No signup, nothing to install.

Most people meet Actions billing for the first time when something stops working — a wall of red checks, or a number on an invoice nobody expected. The rules are not complicated, but three of them are counter-intuitive enough that a workflow can cost five or ten times what its author assumed.

1. Private repositories draw on a monthly allowance

Each plan includes a number of minutes per month. Public repositories run free on standard runners and have no allowance to exhaust.

PlanIncluded minutes / month
Free2,000
Pro3,000
Team3,000
Enterprise Cloud50,000

Past the allowance you are billed per minute, if a spending limit allows it. If it does not, jobs simply stop being assigned runners — which is what the wall of red usually is.

2. The operating system multiplies what you spend

This is the first surprise. A minute is not a minute: what a standard runner draws from your allowance depends on the image.

RunnerAllowance minutes per wall minute
Linux (ubuntu-*)1×
Windows (windows-*)2×
macOS (macos-*)10×

A ten-minute test suite on macOS spends a hundred allowance minutes. On the Free plan, twenty such runs consume the entire month. The common shape that triggers this is not a deliberate choice at all — it is runs-on: ${{ matrix.os }} over an OS matrix, where the macOS leg quietly outspends the other two put together.

3. Every job is rounded up, separately

The second surprise. Rounding is per job, not per workflow run. Thirty matrix jobs of twenty seconds each are billed as thirty minutes, not ten — the rounding costs three times the work.

This makes a wide matrix of very short jobs one of the worst value shapes available. Combining several axis values into a single job that loops over them removes the rounding entirely.

4. Larger runners are billed differently

Larger GitHub-hosted runners (ubuntu-latest-8-cores and similar) are charged per minute from the first minute and do not draw on the included allowance. They do not make you run out of minutes; they appear on the bill instead. Self-hosted runners are free to Actions — you are paying for the hardware somewhere else.

5. A job with no timeout can run for six hours

The third surprise, and the one that does the most damage in a single incident. If a job does not set timeout-minutes, GitHub lets it run for 360 minutes before killing it. One hung job — a test waiting on a port that never opens, a command sitting at an interactive prompt — bills 360 minutes on Linux, and 3,600 on macOS. That single wedged job exceeds the entire monthly allowance of every plan below Enterprise.

Setting timeout-minutes a little above a job's normal runtime costs nothing and caps the worst case.

6. Two events can build the same commit twice

A workflow that triggers on both push and pull_request, with no branches: filter on push, runs twice for every commit pushed to a branch with an open pull request. Both runs are genuine, so nothing in the billing page marks either as waste. Restricting push to your default branch keeps the main-branch run and drops the duplicate.

Relatedly, without a concurrency group set to cancel-in-progress: true, pushing three times in a row leaves the first two runs going to completion and billing in full, even though nobody will look at their results.

7. Scheduled workflows bill whether or not anything changed

A cron of */20 * * * * is about 2,190 runs a month. At three minutes a run that is 6,570 allowance minutes — more than twice the Team allowance — accruing on a repository nobody is touching. Scheduled spend is the easiest kind to forget, because no pull request is ever associated with it.

Questions

Why does a macOS minute cost ten times a Linux minute?

GitHub applies a multiplier per runner image when drawing down your included allowance: Linux 1x, Windows 2x, macOS 10x. The wall-clock time is the same; what differs is how many allowance minutes it consumes. A ten-minute test suite on macOS spends a hundred allowance minutes, so twenty such runs consume the entire Free plan month.

Does a job that takes twenty seconds cost twenty seconds?

No. Each job is rounded up to the next whole minute, and the rounding is per job rather than per workflow run. Thirty matrix jobs of twenty seconds each bill thirty minutes, not ten, so the rounding costs three times the work. Combining several matrix axis values into one job that loops over them removes it.

How long can a job run if I do not set timeout-minutes?

360 minutes. GitHub kills it only at that point, so one hung job — a test waiting on a port that never opens, a command sitting at an interactive prompt — bills 360 minutes on Linux and 3,600 on macOS. That single wedged job exceeds the entire monthly allowance of every plan below Enterprise.

Do larger runners use up my included minutes?

No. Larger GitHub-hosted runners are charged per minute from the first minute and do not draw on the included allowance, so they appear on the bill rather than making you run out of minutes. Self-hosted runners are free to Actions — you are paying for that hardware elsewhere.

Why would the same commit be built twice?

A workflow that triggers on both push and pull_request, with no branches filter on push, runs twice for every commit pushed to a branch with an open pull request. Both runs are genuine, so nothing in the billing page marks either as waste. Restricting push to your default branch keeps the main-branch run and drops the duplicate.

Want this arithmetic run against your own workflows?

Paste your workflow YAML and get projected monthly minutes and cost, attributed per workflow and per job, with these patterns flagged where they appear.

Estimate your Actions cost

Free, runs entirely in your browser, nothing is uploaded. There is a zero-dependency CLI for your own CI too.