GitHub Actions Artifacts Quietly Eat Your Storage Budget: How Retention Actually Works
GitHub Actions artifacts default to 90-day retention, which quietly inflates storage for noisy pipelines. How per-artifact retention-days, org caps, and API-based cleanup actually work.
22 Jul 2025, 12:56 UTC

Your CI pipeline uploads a test report, a coverage bundle, and a debug build on every run. Six months later, the repository's Actions storage is bloated, and nobody can say why. The answer is usually artifact retention: GitHub deletes workflow artifacts automatically, but the default window is long enough that high-volume pipelines accumulate a surprising amount of data before anything is purged.
The useful takeaway: retention is configurable at three levels — organization/repository settings, per workflow run, and per artifact — and the per-artifact setting is the one that gives you the most control for the least effort. Setting it takes one line of YAML.
The default: 90 days, then automatic deletion
Unless you change it, artifacts from a workflow run are kept for 90 days and then removed automatically. Repository and organization admins can shorten (or lengthen, within plan limits) this default in the Actions settings. For a repository, that's under Settings → Actions → General → Artifact and log retention. Note that this setting historically covers both artifacts and logs together, so check the current UI labels before assuming you can tune them separately.
Two things about the default catch teams off guard:
- 90 days is a long time for noisy artifacts. A matrix build producing 12 coverage archives per push, several pushes a day, adds up fast — and artifact storage counts toward your plan's storage quota, with overage billed on paid plans.
- Retention changes are not retroactive. A new policy applies to artifacts created after you set it. Everything already stored keeps its original expiry. If you're tightening retention to cut costs, expect to also do a manual cleanup pass on old artifacts.
Per-artifact retention: the one-line fix
The most practical lever is the retention-days input on the upload action. It overrides the repository default for that specific artifact:
- uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7
- uses: actions/upload-artifact@v4
with:
name: release-bundle
path: dist/
retention-days: 90The reasoning behind this split: a test report is only useful while the run is fresh — nobody opens a Playwright trace from a build three weeks ago. A release bundle attached to a tagged build, on the other hand, may need to survive for audit or rollback purposes. Treating both the same is how storage bills grow.
A sensible starting taxonomy:
- 1–7 days: test reports, screenshots, linter output, debug logs.
- 14–30 days: build intermediates you might need to diagnose a flaky failure.
- 90 days (or the repo default): release candidates, signed binaries, compliance evidence.
One constraint worth knowing: retention-days can't exceed the repository or organization maximum if an admin has capped it. In an org with a 30-day ceiling, asking for 90 silently clamps to the cap — so verify against your org settings rather than assuming the YAML wins.
Auditing what's actually stored
Before changing anything, see what you're paying for. You can list artifacts with the REST API from any machine with a token that has actions:read on the repo:
curl -H "Authorization: Bearer <TOKEN>" \
"https://api.github.com/repos/<OWNER>/<REPO>/actions/artifacts?per_page=100"Each entry includes size_in_bytes, created_at, and expires_at. Sorting by size quickly reveals the offenders — it's usually one workflow uploading something huge on every run. Replace <TOKEN> with a fine-grained personal access token or a GitHub App token; don't paste tokens into shared scripts or CI logs.
For cleanup of artifacts that predate a new policy, the DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id} endpoint removes individual artifacts. This requires actions:write permission (in practice, admin or a token with that scope). Deleting an artifact does not touch the workflow run itself — the run history, logs, and status remain. Still, deletion is permanent, so script it against a filtered list (e.g., older than a date, larger than a threshold) rather than wiping everything.
Trade-offs and limitations
- Short retention hurts debugging. If a flaky test fails overnight and you look at it Monday, a 1-day retention window means the evidence is gone. Match the window to how quickly your team actually triages failures.
- No retroactive enforcement. As noted, tightening policy doesn't shrink existing storage. Budget one manual cleanup cycle.
- Artifacts aren't releases. If something must survive indefinitely, attach it to a GitHub Release instead — release assets don't expire. Artifacts are the wrong tool for long-term distribution.
- Fork PRs and reruns create new artifacts. Rerunning a workflow uploads fresh copies; the old ones still age out on their own schedule, so reruns of heavy pipelines multiply storage until expiry.
What to do this week
Pick your noisiest workflow, add retention-days: 7 to its report uploads, and set the repository default to something deliberate instead of inherited. Then run the API listing above once a month and sort by size — artifact hygiene is a five-minute habit, not a project.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.