Search "GitHub Actions cache pricing" and half the results will tell you overage costs $0.25 per GB per month. That number is real — it's just for the wrong thing. $0.25/GB/month is what GitHub charges for shared storage (build artifacts and Packages). The Actions cache specifically, the thing every actions/cache step in your workflow file writes to, overages at $0.07/GB/month. Both numbers sit in the same row of GitHub's own billing documentation, and enough blogs and pricing calculators have copied the wrong one into "cache pricing" roundups that it's now the more commonly repeated figure.
The real story isn't the price, though — $0.07/GB/month turns out to be genuinely cheap. It's that GitHub spent the last nine months converting a resource every workflow treated as free, unlimited, and invisible into three separate, independently enforced limits: a metered storage overage, a peak-hourly billing model, and — as of January 2026 — a hard rate limit on how fast you're allowed to write to it. The storage bill is the least of it.
The SKU Everyone Gets Wrong
GitHub Actions cache started as a flat, unmetered convenience: every repository got 10GB, cache entries fell off after 7 days of no use, and if you went over 10GB, GitHub just evicted your oldest entries to make room. Free, but with a hard ceiling you never got to raise.
That changed in two steps:
| Date | Change |
|---|---|
| Nov 20, 2025 | Cache storage becomes pay-as-you-go past the 10GB free tier, on a Pro/Team/Enterprise account. Overages are billed per GB/month instead of silently evicting your oldest entries. |
| Jan 16, 2026 | A new rate limit caps cache-entry uploads at 200 per minute per repository, independent of storage size. Exceeding it returns a 429 and a Retry-After header; downloads are unaffected. |
The 10GB free allowance didn't go away — it's still there, at no cost, on every plan. What changed is what happens once you cross it: instead of GitHub quietly deciding what to evict on your behalf, you now get billed for the extra room, and separately, you're capped on how fast you're allowed to write new entries regardless of how much room you have left.
Here's the table that actually matters, straight from GitHub's billing docs, because the SKU-name confusion is exactly how the wrong number gets repeated:
| Storage type | Price / GB / month |
|---|---|
| Shared storage (artifacts + GitHub Packages) | $0.25 |
| Actions cache | $0.07 |
| Custom image storage | $0.07 |
If you've seen "$0.25/GB" attached to cache specifically, it's this table's first row leaking into summaries of the second.
What the Meter Actually Costs
Cache storage billing works on peak-hourly usage: GitHub samples your repository's cache footprint every hour, takes the peak for that hour, and accrues those peaks into a monthly charge — the same model Actions already uses for artifact and Codespaces storage. It's not "your average cache size times a rate"; a repository that spikes to 80GB for two hours during a busy afternoon and sits at 12GB the rest of the day gets billed for that spike, not smoothed against the quiet hours.
Run the math on two realistic teams and the sticker shock mostly evaporates:
A normal-sized team. Five to eight active feature branches, each caching node_modules plus a Playwright/Cypress browser download — call it 2GB per branch. Peak footprint: 12-16GB. Overage: 2-6GB. Monthly cost: $0.14–$0.42. Effectively free, which is exactly why nobody who runs a normal-sized repo has noticed any of this.
A monorepo running matrix builds. A 30-package TypeScript monorepo, Turborepo or Nx caching build output per package, tested across a 3-version Node matrix, with 20 concurrent PR branches — the shape that stresses a cache backend hardest, because every package/version/branch combination writes its own entry. Even a modest 150MB per package-per-version-per-branch adds up: 30 × 3 × 20 × 0.15GB ≈ 270GB at peak. Overage: ~260GB. Monthly cost at $0.07/GB: ~$18.
Eighteen dollars a month for a 30-package monorepo running real matrix CI is not a line item anyone budgets around. That's the honest finding here: GitHub didn't turn cache storage into a meaningful cost center. It turned it into a rate-limited one, and that's a different problem with a much bigger bill attached.
The Rate Limit Is the Expensive One
The 200-uploads-per-minute cap landed because GitHub's cache backend was getting hammered by exactly the monorepo/matrix pattern above — dozens of jobs across dozens of branches all trying to write a fresh cache entry inside the same sixty-second window, a pattern GitHub's own changelog calls "cache thrash." Node.js's core repository and multiple Nix-community projects (cache-nix-action, the Magic Nix Cache action) hit the limit hard enough that GitHub engineers showed up directly in their GitHub issues to help triage it, which is a reasonable proxy for "this wasn't a hypothetical edge case."
Here's why that cap is the part that actually costs money. A rejected cache upload doesn't fail your build — the job that tried to write it just finishes normally with an uncached result. The damage lands on the next job that needed to read that cache and can't, because it was never written. Instead of a warm cache hit, that job pays for a cold install or a full rebuild from scratch, and it pays for it on GitHub's other meter — the one that was never cheap:
| Runner | Per-minute rate (post-Jan 2026 cut) |
|---|---|
| Linux 2-core | $0.006 |
| Windows 2-core | $0.010 |
| macOS 3-4 core | $0.062 |
A cold node_modules install or an uncached monorepo package rebuild routinely costs several extra minutes per job. Multiply that by even a modest number of jobs caught mid-thrash across a busy PR wave, and the wasted runner-minutes from a handful of rejected cache uploads outrun the entire monthly storage overage from the previous section in a single incident — worse on Windows or macOS runners, where the per-minute rate is 1.7x to 10x the Linux baseline. The $0.07/GB storage fee was never the expensive line. The rate limit that forces a cold rebuild onto the per-minute meter is.
GitHub's own mitigation advice — implement exponential backoff, update the caching actions you depend on to handle 429s gracefully — is sound, but it's engineering work every team hitting the limit now has to do on top of the CI pipeline they already had, just to get back to the reliability a flat, unmetered cache used to hand them for free.
What a Self-Hosted Cache Volume Skips Entirely
The reason GitHub needs a rate limit at all is structural, not a tuning mistake: Actions cache is one shared backend serving every public and private repository on the platform, so one repo's upload burst is contention every other repo's build has to wait behind. That's the trade-off of a multi-tenant cache API serving millions of unrelated repositories through one endpoint.
A build pipeline running on a Cluster API-managed fleet of owned Hetzner nodes doesn't have that constraint, because it doesn't have that shared backend. A persistent NVMe volume mounted into the build path is local disk on a machine the platform already owns — there's no per-GB meter because there's nothing being metered against a shared quota, and no upload-rate cap because there's no cross-tenant contention on a single centralized write path for an unrelated fleet's build traffic to queue behind.
The storage itself isn't free in an absolute sense — Hetzner's NVMe-equipped dedicated servers run in the neighborhood of $0.04-0.07 per GB per month for the raw disk. But that cost is bundled into a server the platform is already renting or has already bought to run builds on in the first place; it's not a second, separate bill that scales with cache footprint the way GitHub's overage does. The comparison isn't "$0.07/GB from GitHub vs. $0/GB self-hosted." It's "$0.07/GB as a recurring, metered line item that grows with usage vs. storage that's a sunk cost of hardware you needed anyway."
| GitHub Actions cache | Self-hosted NVMe cache volume | |
|---|---|---|
| Free allowance | 10GB/repo | Whatever the node's disk has spare |
| Overage | $0.07/GB/month, billed on hourly peaks | None — it's local disk, already paid for |
| Upload rate limit | 200 entries/minute/repo (Jan 2026+) | None — no shared backend to protect |
| Eviction | LRU past 7 days idle, or past storage cap | Whatever GC policy the platform sets |
| Failure mode under load | 429s → cold rebuilds → runner-minute cost | Contention is a capacity-planning problem, not an API limit |
That last row matters: a self-hosted fleet running many tenants on shared build nodes still has to think about fairness — one tenant's cache shouldn't be allowed to fill a shared node's disk and starve everyone else's GC headroom. But that's a scheduling and garbage-collection decision the platform controls directly, not a rate limit imposed by a vendor's shared infrastructure that every unrelated repository on the same platform is also competing against.
The Pattern Keeps Repeating
GitHub Actions cache joins a list this space keeps adding to: bundled conveniences that start free and unmetered, then get a meter bolted on once usage at platform scale makes "free and unlimited" too expensive for the vendor to keep offering everyone. It happened to GitHub's own artifact storage, to egress on more than one PaaS, and now to cache. None of that makes GitHub's move unreasonable — a shared backend serving millions of repositories has to be protected somehow, and $0.07/GB is not an aggressive price. But it's one more place where "hosted and free" quietly becomes "hosted and metered," and the fix isn't outrage at the price — it's noticing that the underlying resource (disk, next to compute) was never actually scarce. It was just being rationed across everyone else's builds too.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. A persistent build cache that lives on disk you already control, with no shared quota and no vendor rate limit standing between a job and the cache it needs, is the kind of thing an owned fleet gets to have by default instead of engineering around. Star the repo on GitHub or deploy your first app today.



