Attributing GitHub Actions Cost per Repository and Team
GitHub Actions bills minutes as account level SKU totals. Attribute cost per repository and team with per job billing rows, runner labels and BYOC tags.
Last verified:
GitHub Actions cost per repository is not a number the Actions bill hands over: minutes arrive as SKU totals for the whole account, with no repository, workflow, or runner label attached to them. Per repository and per team attribution needs a per job record, which on WarpBuild is the CI tab of the Billing report, where every row is one job execution carrying its repository, job name, runner label, stack, snapshot usage, execution time, billed time and a runner plus snapshot cost split.
This guide covers why an account level total stalls the conversation, which columns turn a bill into a chargeback table, how BYOC pushes the same attribution into your own cloud bill through stack tags, and a worked monthly model for three teams sharing one account.
Diagnosis
An Actions invoice is a short list of numbers. GitHub's usage report, downloadable from billing settings, is organized around SKUs: standard Linux minutes, Windows minutes, macOS minutes, and a line for each larger runner tier. That tells you the account spent more this month than last. It does not tell you which team should change something.
The unit is minutes by SKU. A tier total is the sum of every workflow in the account that touched that tier. When the 8-core Linux line grows by 40,000 minutes, the number is real and the owner is unknown, so the follow-up meeting produces a request for everyone to be careful rather than a change to one workflow. The usual suspects behind a jump are covered in why is my GitHub Actions bill so high.
Monorepos defeat repository level attribution. Plenty of organizations run six teams out of one repository. Even a report that names the repository stops one level above the unit that matters, because the mobile team and the data team share the repository and share the line.
Re-runs and matrix fan-out disappear into the aggregate. A matrix that expands to 24 legs bills 24 jobs. A flaky suite that forces whole workflow re-runs bills each attempt in full. Both land inside the same monthly minutes number as the work that shipped, so waste and output are indistinguishable at the SKU level.
Chargeback needs a stable key and a fixed rate. To cross-charge a team you need a row you can point at, a key that survives a workflow rename, and a per minute price that does not move between months. Pricing on WarpBuild is purely usage based. There is no base subscription fee, no platform fee, and no seat fee, so the rate on the chargeback line is the published per minute rate for the label the job ran on, and nothing gets allocated by seat count.
Fix
Attribution comes from three surfaces plus an export step. Most teams use the first two together.
Read cost per job in the CI billing report
The Reports page splits billing into CI, Docker Builder and Cache tabs. The CI tab carries the per job detail:
- A daily stacked bar chart of cost, grouped by repository or by runner label.
- Summary cards for total cost, runner cost, snapshot cost and total jobs across the selected period.
- A table where each row is a single job execution: repository, job name, runner label, stack, snapshot usage, execution time, billed time and the cost split between runner and snapshot.
- Filters on repository, runner label, stack, job name and snapshot usage.
The split between execution time and billed time is what makes the row auditable, and the split between runner cost and snapshot cost keeps snapshot restores visible instead of folding them into a machine rate. The Docker Builder and Cache tabs attribute their own usage per session and per cache entry.
The Jobs section aggregates per unique repository, workflow and job name combination, with run count, success rate, duration at P75 and P90, queue time at P75 and P90, and CPU and memory at P75 and P90. That view answers the second question a chargeback conversation raises, which is whether the expensive team is expensive because of volume or because of size.
Make the runner label and stack the chargeback key
Every job records the label it ran on. Giving each team its own label, or its own stack on BYOC, converts the label column into the chargeback dimension, and it survives workflow renames and directory moves in a way that path based heuristics do not.
Splitting labels by team is a bookkeeping decision with no capacity cost attached. Run as many jobs as your workflows need. Generally available Linux and Windows runners do not have plan-level concurrency caps, so a per team label does not carve a fixed pool into smaller pieces.
Attribute BYOC spend inside your own cloud bill
BYOC runs on AWS, GCP, and Azure, and there the compute lands on your own cloud invoice, so attribution has to happen in the cloud provider's cost tooling. Stacks accept custom tags, and tags set on the stack are applied to every resource the stack creates, which is what cost allocation reports group by. WarpBuild also adds managed-by: warpbuild to provisioned resources, usable for IAM tag based access control.
Custom resource tags are available on AWS today. The GCP and Azure BYOC configuration docs list resource tagging under coming soon, so plan cloud side attribution on those clouds around account or project boundaries for now.
The Terraform provider covers BYOC on AWS, currently in beta for runner images and custom runner sets, so a per team runner set can be defined in code next to the rest of your infrastructure.
Export it on a schedule
Every report tab exports CSV containing all rows matching the current filters and sort order rather than the visible page, which is the shape a finance system wants. The reports API lets the same export run as a scheduled job and write into the chargeback ledger. The endpoints and API key scopes behind that export are covered in pulling GitHub Actions metrics from the API.
Configuration
Two changes make the billing rows attributable. Give each team a distinct label, and give each job a name that reads correctly in a CSV row with no workflow context around it.
name: platform-ci
on:
pull_request:
push:
branches: [main]
concurrency:
group: platform-ci-${{ github.ref }}
cancel-in-progress: true
jobs:
platform-unit-tests:
runs-on: warp-ubuntu-latest-x64-4x
steps:
- uses: actions/checkout@v4
- run: make test
platform-integration-tests:
runs-on: warp-ubuntu-latest-x64-16x
steps:
- uses: actions/checkout@v4
- run: make integration
web-unit-tests:
runs-on: warp-ubuntu-latest-x64-2x
steps:
- uses: actions/checkout@v4
- run: npm ci && npm testThree jobs, three sizes, three rows per run in the CI billing table, each with the team prefix in the job name and the size in the label. Filtering that table on job name platform- returns exactly the platform team's minutes and cost.
On BYOC, the attribution keys live in the cloud tags instead. These keys are added by default to every resource a stack creates, per the AWS BYOC configuration docs:
| Tag key | Value |
|---|---|
| warpbuild-managed-by | warpbuild |
| Name | {runner-id} |
| warpbuild-github-org | {github-org} |
| warpbuild-runner-labels | {runner-label1}, {runner-label2}, ... |
| warpbuild-runner-id | {runner-id} |
| warpbuild-stack-id | {stack-id} |
| warpbuild-stack-name | {stack-name} |
Custom tags you add on the stack sit alongside these. A cost-center or team tag activated as a cost allocation tag in your cloud account gives the same breakdown on the cloud invoice that the runner label gives inside the CI billing table.
Cost or Time Model
Three teams share one account. The job mix below is the input; every rate comes from the runner catalog on the pricing page.
| Team | Label | Jobs per month | Avg billed minutes | Minutes | Rate | Runner cost |
|---|---|---|---|---|---|---|
| Platform | warp-ubuntu-latest-x64-4x | 4,000 | 6 | 24,000 | $0.008 | $192.00 |
| Web | warp-ubuntu-latest-x64-2x | 9,000 | 5 | 45,000 | $0.004 | $180.00 |
| Data | warp-ubuntu-latest-x64-16x | 600 | 18 | 10,800 | $0.032 | $345.60 |
| Total | 13,600 | 79,800 | $717.60 |
The data team restores a snapshot on each of its 600 jobs at $0.04 per restore, adding $24.00 that the billing table reports as the snapshot half of its cost split. The chargeback table for the month:
| Team | Runner cost | Snapshot cost | Total | Share of bill |
|---|---|---|---|---|
| Platform | $192.00 | $0.00 | $192.00 | 26 percent |
| Web | $180.00 | $0.00 | $180.00 | 24 percent |
| Data | $345.60 | $24.00 | $369.60 | 50 percent |
| Total | $717.60 | $24.00 | $741.60 | 100 percent |
The result is the useful part. The web team runs 9,000 of the 13,600 jobs and owns 24 percent of the bill, while the data team runs 600 jobs and owns half of it, because one data job bills $0.576 and one web job bills $0.02. That shape is invisible in a SKU total and obvious in a per job export, and it says where to send the right-sizing work described in cutting GitHub Actions costs without rewrites.
For the same job mix on GitHub-hosted runners at list prices, from GitHub's minute multipliers reference, checked on 2026-08-13:
| WarpBuild label | Rate | GitHub-hosted equivalent | GitHub list rate | Difference |
|---|---|---|---|---|
| warp-ubuntu-latest-x64-2x | $0.004 | ubuntu-latest, 2 vCPU on private repositories | $0.006 | 33 percent lower list price |
| warp-ubuntu-latest-x64-4x | $0.008 | 4-core Linux larger runner | $0.012 | 33 percent lower list price |
| warp-ubuntu-latest-x64-8x | $0.016 | 8-core Linux larger runner | $0.022 | 27 percent lower list price |
| warp-ubuntu-latest-x64-16x | $0.032 | 16-core Linux larger runner | $0.042 | 24 percent lower list price |
| warp-ubuntu-latest-x64-32x | $0.064 | 32-core Linux larger runner | $0.082 | 22 percent lower list price |
Applying those list prices to the same minutes gives $288.00 for the platform team, $270.00 for the web team and $453.60 for the data team, or $1,011.60 against the $717.60 of runner cost above, a difference of $294.00 per month at an identical job mix and identical machine shapes.
The table above compares rates alone. To estimate a fleet total, combine current per-size rates with measured job durations. The hosted versus BYOC guide covers the additional cloud-cost inputs for a BYOC fleet.
Two line items stay outside the per job model. SSO is available for a flat $250 per month, whatever the user count, and cache storage bills at $0.20 per GB month, which spans jobs rather than belonging to any one of them. Everything else on the invoice traces to a row in the CI billing table. Signup includes $10 free credits, which is enough to run a week of one team's jobs through the report and check the numbers against your current bill before committing.
FAQ
Can I get GitHub Actions cost per repository without changing anything about my runners?
Only down to the SKU. A minutes total for standard Linux or for an 8-core tier covers every workflow in the account that used it, so the finest unit you can defend is the SKU rather than the repository. Cost per repository needs a per job record: the WarpBuild CI billing table writes one row per job execution with repository, job name, runner label, stack, snapshot usage, execution time, billed time and the runner plus snapshot cost split.
How do I split cost between two teams that share one monorepo?
Filter below the repository. The CI billing table filters by repository, runner label, stack, job name and snapshot usage, so a job name prefix per team, or a distinct runner label per team, turns into the chargeback key. Export the filtered view as CSV and the sum of the cost column is that team's line for the month.
Does cost attribution work on BYOC runners?
Yes, and it moves into your own cloud bill. Tags set on a stack are applied to every resource the stack creates, on top of the default keys such as warpbuild-stack-name, warpbuild-runner-labels and warpbuild-github-org, so cost allocation reports in your cloud account group runner spend by whatever dimension you tagged. Custom resource tags are available on AWS today; GCP and Azure list resource tagging as coming soon.
What appears on a WarpBuild bill that cannot be attributed to a single job?
Very little. Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee, and runners bill per minute against the job that used them. The one flat line is SSO, available for a flat $250 per month whatever the user count. Cache storage bills per GB month, which spans jobs rather than belonging to one.
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.