← FinOpsAid blog

Why is storage on the GitHub bill at all? Artifacts, packages and the retention nobody set
Nobody provisions GitHub storage, which is why it grows. Where the gigabytes come from — artifacts, packages, Codespaces — and the one setting that stops it.
Storage is the line item people are most surprised to find on a GitHub bill, and the reason is that nobody ever decided to buy any. There's no provisioning step, no capacity request, no moment where someone approves a number. It accrues as a by-product of building software, under retention defaults that shipped with the product and that almost nobody has revisited.
That makes it unusual among cost surfaces. Most spend grows because someone chose to do more of something. Storage grows because nothing was chosen at all — and it only ever grows, because deletion is a thing that has to be configured rather than a thing that happens.
Where the gigabytes actually come from
Four sources, in roughly the order they surprise people.
Actions artifacts. Every workflow run that uploads an artifact keeps it for the retention period, and the default is generous. A pipeline producing a few hundred megabytes per run, several times a day, across a dozen active repos, is writing a substantial amount of storage every week and deleting none of it until retention expires. This is usually the largest and most fixable component.
Packages and container images. Container registries are where storage goes to accumulate permanently. Every image tag is retained, including every intermediate build tag from every CI run — and because tags are cheap to create and nobody prunes them, a registry easily ends up holding thousands of images of which a handful are ever pulled again.
Codespaces disks. Billed per gigabyte-month for as long as the environment exists, which — as the two-meter problem makes clear — is not the same as for as long as it runs.
Git LFS. Smaller for most organizations but genuinely permanent, and it carries bandwidth charges alongside storage. Large binaries committed once stay forever.
Repository content itself is rarely the issue. When storage costs move, it's nearly always artifacts or packages.
Retention is the whole game
Almost every storage problem is a retention problem wearing a different hat, and retention has one property that makes it unusually good value to fix: it's set once and applies forever, to every future run, with no engineering work per repository.
Artifact retention can be set at the organization level and overridden per workflow. Shortening it from the default to something matched to how artifacts are actually used — which for most build outputs is measured in days, not months — reduces the standing volume proportionally and permanently. The question worth asking each team is simple: when did you last download an artifact more than a week old? For debug logs and test outputs the honest answer is usually never.
Package retention needs a policy rather than a number, because not all tags are equal. The pattern that works is keeping release-tagged images indefinitely, keeping the most recent N builds per branch, and expiring untagged and intermediate images aggressively. Untagged images in particular are almost always pure waste — they're the leftovers of a tag being moved, and nothing references them.
Why nobody notices until it's large
Storage has the worst possible cost signature for getting attention: small, continuous, and monotonic.
It never spikes, so anomaly detection doesn't fire on it — a series that grows 3% every month is behaving exactly as its baseline predicts. It's rarely the largest line, so it doesn't lead a review. And it has no owner, because it isn't produced by a decision anyone made. The result is a line item that quietly compounds for two years and then appears in a cost review as a number nobody can explain.
The signature to watch for is a category whose absolute size is unremarkable but whose trend is relentlessly upward while headcount is flat. That's storage almost every time.
This is the one cost shape that defeats every detection method you have. Deviation-based alerting sees a series behaving exactly as predicted; threshold alerting sees a line that never crosses. Only the slope gives it away. Illustrative series.
Attribution, and where it stops being clean
Artifacts and packages attribute well: both belong to a repository, and a repository usually belongs to a team. That puts most of your storage cost in the same defensible bucket as Codespaces, where the finding comes with an owner attached.
Where it degrades is anything shared — an org-wide registry, a common artifact bucket, LFS objects referenced from multiple forks. Those need an allocation rule, and the resulting per-team number is modeled rather than billed. Worth stating on the number itself, per the same discipline as everything else that's apportioned rather than invoiced.
A storage review that pays for itself
- Break the total down by category first. Artifacts, packages, Codespaces, LFS. The shape tells you where to look.
- Check the artifact retention setting. If nobody has changed it, that's your single biggest lever and it takes a minute.
- Find untagged and intermediate images. Almost always deletable, often a large share of registry volume.
- Ask when an old artifact was last downloaded. The answer usually justifies a much shorter retention than anyone expected.
- Watch the trend, not the level. Storage never spikes; a flat-headcount org with rising storage is accumulating, not growing.
- Set the policy, don't run the cleanup. A one-off purge buys you a quarter. Retention buys you every quarter.
Storage will never be your biggest GitHub line. It's worth fixing anyway, because it's the cheapest permanent win on the platform: one org-level setting, no engineering trade-off, and it applies to work nobody has done yet.
FinOpsAid itemizes it: Storage breaks spend down by category and SKU with a spend-over-time trend, so the slow accumulation is visible as a slope rather than discovered as a total — every figure stamped with the sync it came from.
Frequently asked questions
What is actually on a GitHub storage bill?
Several unrelated things that share a line: Actions artefacts, Actions cache, Packages, Git LFS and Codespaces disks. Each is created by a different process and fixed by a different team, which is why 'reduce storage spend' fails as a single action item.
Why does GitHub storage cost keep growing?
Because storage bills for existence rather than activity, and nothing in GitHub ever prompts anyone to delete. Retention defaults are generous, the person who created an artefact has moved on, and the cost only falls when someone deliberately removes data.
Can I see GitHub storage cost per repository?
Not in general. GitHub reports storage at organization level by category and SKU. The one exception is Actions cache storage, which does break down per repository — which also makes it the one storage line with an owner you can send a finding to.
Want to see what your storage is actually made of? Connect your GitHub org — read-only and free during beta — or explore the demo dashboard first.