Skip to content

Blog

GitHub Actions pricing in 2026: rates, rounding and where the bill leaks

·

GitHub announced two changes to Actions pricing on 16 December 2025. The first, a cut of up to 39 per cent to hosted-runner rates from 1 January 2026, is reflected in the rates GitHub publishes today. The second, a new charge on self-hosted runners, was postponed within days. A lot of what is still written about Actions pricing predates one or both.

This post sets out the rates, the allowances and the rounding rule as GitHub publishes them, read from docs.github.com on 10 November 2026, and then looks at the places a bill most often leaks. Every GitHub figure below is linked and dated, because these are figures that move. Check the linked page before you act on one.

How a private-repository bill is built

Included usage is a dollar amount, not a number of minutes. Each plan comes with a monthly amount of compute, and each job draws it down at the per-minute rate of the runner it used. When the amount is gone, usage is billed at those same rates. GitHub’s own guide is that $300 covers 50,000 minutes on a standard Linux runner at $0.006 a minute. The same arithmetic gives 2,000 minutes for $12 and 3,000 for $18. A Windows or macOS minute draws down more, so the allowance buys fewer of them.

PlanIncluded compute per monthStandard Linux 2-core minutes it buysArtifact storage
GitHub Free$122,000500 MB
GitHub Pro$183,0001 GB
GitHub Free for organizations$122,000500 MB
GitHub Team$183,0002 GB
GitHub Enterprise Cloud$30050,00050 GB

Standard GitHub-hosted runners are free in public repositories, and for GitHub Pages and Dependabot. GitHub’s billing page also lists self-hosted runners as usage that “remains free independently and does not reduce the included compute usage”. Larger runners are the exception: they are billed in public repositories too, and are only available to organisations on Team or Enterprise Cloud. Usage is charged to the repository owner, not to whoever triggered the run.

An account with no payment method on file is blocked once it has used the included amount. With one, spending can be limited by budgets, which come up again below.

The rule that shapes everything else is rounding, and it is per job. In GitHub’s words: “GitHub rounds the minutes and partial minutes each job uses up to the nearest whole minute.” A job that runs for 61 seconds is billed for two minutes, and a job that runs for four seconds is billed for one.

What changed on 1 January 2026

GitHub’s roadmap item for the cut said that on 1 January 2026 it would reduce the price of GitHub-hosted runners by up to 39 per cent, with the free monthly quotas unchanged. These are the rows most teams use, with the figures from that item. The “after” column matches the rates on GitHub’s pricing page today.

RunnerPer minute, beforePer minute, afterChange
Linux 1-core$0.002$0.0020%
Linux 2-core$0.008$0.006-25%
Windows 2-core$0.016$0.010-38%
macOS 3-core or 4-core$0.080$0.062-23%
Larger Linux x64, 4-core$0.016$0.012-25%
Larger Linux x64, 8-core$0.032$0.022-31%
Larger Linux x64, 16-core$0.064$0.042-34%
Larger Linux x64, 32-core$0.128$0.082-36%
Larger Linux x64, 64-core$0.256$0.162-37%
Larger Linux x64, 96-core$0.384$0.252-34%

The item describes the cut as a reduction of base rates “paired with a new $0.002 per-minute GitHub Actions cloud platform charge”, and says that for GitHub-hosted runners that charge is “already included in the reduced meter price”. So the hosted rates in this post are the all-in rates. Nothing is added on top.

The self-hosted charge that was postponed

The same announcement said that from 1 March 2026 the $0.002 per-minute platform charge would apply to self-hosted runner usage in private repositories, and not to public repositories or GitHub Enterprise Server. On 19 December 2025 a GitHub reply on that roadmap item said: “We’re postponing the announced billing change for self-hosted GitHub Actions to take time to re-evaluate our approach.” The item was closed the same day, and the changelog post carries the same notice without a date.

As of 10 November 2026, GitHub’s billing documentation still lists self-hosted runners as free, and we found no announcement of a new date. That is a status, not a forecast. If the charge matters to your planning, read GitHub’s changelog post and the billing page itself rather than a summary of either, this one included.

The per-minute rates today

These are the rates on GitHub’s Actions runner pricing page, read on 10 November 2026. The page prints no effective date. Sizes above two cores are larger runners.

CoresLinux x64Linux arm64Windows x64
1 (single-CPU, ubuntu-slim)$0.002--
2$0.006$0.005$0.010
4$0.012$0.008$0.022
8$0.022$0.014$0.042
16$0.042$0.026$0.082
32$0.082$0.050$0.162
64$0.162$0.098$0.322
96$0.252-$0.552

Three more rows sit outside that grid. A standard macOS runner (3-core or 4-core) is $0.062 a minute and the 12-core x64 macOS runner is $0.077. GPU-powered larger runners are $0.052 a minute for Linux 4-core and $0.102 for Windows 4-core. GitHub says larger runners are billed only for the time workflows run on them, with no charge for creating one that sits unused.

Two details decide which row you land on. A standard Linux runner has 2 vCPUs and 8 GB of memory in a private repository, and 4 vCPUs and 16 GB in a public one. And the single-CPU row is the ubuntu-slim label: 1 vCPU, 5 GB of memory, available in public and private repositories, billed under its own SKU. It runs in an unprivileged container rather than a full VM, so Docker-in-Docker and mounting file systems are not supported, and its job timeout is 15 minutes. That makes it the right place for labelling, linting and other short jobs, and the wrong one for builds.

Read the table as ratios. Against a standard Linux minute, a Windows minute costs about 1.7 times as much, a standard macOS minute about 10 times, and a Linux x64 8-core minute about 3.7 times. Those ratios, more than the dollar figures, are what a matrix multiplies.

Storage: artifacts, packages and caches

Compute is not the only meter. Artifacts and GitHub Packages share one storage allowance (the last column of the plan table above), and beyond it storage costs $0.25 per GB-month. The Actions cache includes 10 GB per repository and costs $0.07 per GB-month beyond that.

Storage accrues every hour, in GB-hours, and is converted to GB-months by dividing by the hours in the month. Cache storage is measured at the peak of each hour. Artifact usage takes 6 to 12 hours to update. The point that surprises people is on the deletion side. In GitHub’s words, deleting artifacts “reduces your current storage and prevents future charges” but “does not remove charges already recorded for the time the storage existed.”

Artifacts and logs are kept for 90 days by default. An organisation can change that to between 1 and 90 days for public repositories and between 1 and 400 days for private ones, and a new setting applies only to new artifacts. Each upload can also set its own retention-days, between 1 and 90.

Rounding is the multiplier nobody models

Take twenty jobs that each run for 61 seconds. The wall-clock total is 20.3 minutes. The billed total is 40, because every job rounds up to two. On a standard Linux runner that is $0.24 rather than $0.12. Nothing about the jobs is wasteful in isolation. The cost is in how many of them there are.

Two habits make this worse. The first is splitting a suite into many short shards: more shards finish sooner, but each one carries a one-minute floor and its own setup. The second is a stream of tiny “gate” jobs, such as a labeller, a title check or a path classifier, each of which bills a minute however briefly it runs. If a job exists only to decide whether another job should run, put it on the single-CPU runner, or fold it into a step of a job that is running anyway.

Reading your usage

Start with one run. Open it from the Actions tab, then choose Usage under “Run details” to see the billable minutes for each job. They are rounded up to the next minute, and they appear only for jobs on private repositories that use GitHub-hosted runners. Public repositories and self-hosted runners show no billable minutes.

For the month, go to Billing & licensing and open the Usage view (labelled Metered usage in places). Filter to Actions, set a time frame and group by SKU: that gives you minutes and cost per runner type. The usage report goes further. Choose “Get usage report” on the Metered usage page and GitHub emails you a link that expires after 24 hours. The detailed report covers up to 31 days and is summed by date, SKU, organisation, repository, cost centre, username and workflow_path, so it is the one that answers “which workflow”. Its amount fields are gross_amount, discount_amount and net_amount, and the discount includes included usage. A month that nets to zero can therefore still show you which workflows used the allowance. The detailed report is available only through the web interface, not the REST usage endpoint.

Actions usage metrics break minutes down by workflow, job, repository, runtime OS and runner type. Access is granted through a custom organisation role that includes the “View organization Actions metrics” permission. Budgets alert at 75, 90 and 100 per cent of the amount, and can be set to stop usage at the limit. That is an option you choose, not the default, and a budget counts only usage from the day it was created. Separately, GitHub emails you when Actions minutes or storage reach 90 and 100 per cent of the plan’s included amount.

Where the bill leaks

Matrices that fan out further than anyone decided. A matrix multiplies its axes, and can generate up to 256 jobs in a run. Suppose a test job runs on Linux, Windows and macOS across three Node versions, and every job takes eight minutes. Nine jobs cost $0.144 for Linux, $0.24 for Windows and $1.488 for macOS: $1.872 a run, of which macOS is 80 per cent. Run all three versions on Linux and only the newest on the other two systems and the run costs $0.72. At 100 runs that is $187.20 against $72.00. The cheap legs give the breadth, and the expensive ones only need to prove the platform.

Retries that re-run what already passed. GitHub’s own example is a workflow that normally takes 10 minutes, fails after 5, and is re-run successfully: the total cost is based on 15 minutes. The failed attempt is not refunded. If a run has six ten-minute jobs and one fails, re-running the whole run bills sixty more minutes. Re-running only the failed job bills ten, plus any job that depends on it.

gh run rerun RUN_ID --failed gh run rerun --job JOB_ID

Idle waits and the default timeout. The meter is a job’s execution time, not its CPU time. A job that polls or sleeps while it waits for a deployment, a queue or a service accrues minutes at the same rate as one that is compiling. And a job with no timeout-minutes defaults to 360, with a hosted-runner ceiling of six hours. A single hung job on a standard Linux runner therefore bills $2.16, on Windows $3.60 and on macOS $22.32, and in a matrix every leg can hang. Set timeout-minutes on every job, to a small multiple of how long it normally takes.

Runners bigger than the job can use. An 8-core Linux x64 runner is about 3.7 times the price of a 2-core one per minute, so it only saves money if the job finishes in under roughly 27 per cent of the minutes. A 4-core runner has to halve the time. Plenty of jobs do not scale like that: installs, linting and anything waiting on a network have a fixed part that more cores never touch. Measure the same job on two sizes before you move a whole workflow.

Artifacts kept for 90 days that nobody downloads after one. A build that uploads 2 GB on each of 50 runs a month holds about 300 GB in steady state at the default retention. At $0.25 per GB-month that is about $75 a month before the allowance. Kept for seven days it is about 23 GB, or under $6. Most artifacts are read once, by the next job in the same run, so the short setting costs nothing you use.

Most of these fixes fit in one workflow file. This one cancels a superseded pull-request run, skips runs that only touch documentation, puts the lint job on the single-CPU runner, trims the matrix, sets timeouts and keeps failure output for a week.

on: pull_request: paths-ignore: - 'docs/**' - '**.md' push: branches: [main] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: contents: read jobs: lint: runs-on: ubuntu-slim timeout-minutes: 10 steps: - uses: actions/checkout@v7 - run: npm run lint test: runs-on: ${{ matrix.os }} timeout-minutes: 20 strategy: matrix: os: [ubuntu-latest] node: [20, 22, 24] include: - os: windows-latest node: 24 - os: macos-latest node: 24 steps: - uses: actions/checkout@v7 - uses: actions/setup-node@v7 with: node-version: ${{ matrix.node }} - run: npm ci && npm test - uses: actions/upload-artifact@v7 if: failure() with: name: test-output-${{ matrix.os }}-${{ matrix.node }} path: test-results/ retention-days: 7

The include entries name an os the base matrix does not contain, so each one adds a single job rather than a column: five jobs instead of nine. Matrices also default to fail-fast: true, which cancels the remaining jobs once one fails. Leave it on unless you need every leg’s result from a failing run. The if: failure() on the upload step means a green run stores nothing.

Before you change any of it, do one thing: pull the detailed usage report for last month and sort by workflow_path. On most accounts a handful of workflows account for most of the spend, and the list tells you which of the five leaks above to look for first.

Our own per-minute rates are on the pricing page, if you want to set them beside GitHub’s.

To put your own numbers through it, the calculator estimates a month, and spending alerts and the spending cap says what you can set on our side to bound it. The ARM64 and Windows minutes that the tables above price separately have their own pages: ARM64 runners and Windows runners. If you decide to move, the migration page is where it starts.

Run your CI and your infrastructure on one bill

Connect GitHub

Card required. You pay only for the minutes your jobs run.