GitLab artifact retention rules for lower storage costs
GitLab artifact retention rules help teams stop old build files from piling up. Learn simple expiry windows by branch and release type.

What goes wrong when artifacts never expire
Every pipeline leaves something behind. A build archive, a test report, a container bundle, a screenshot from a failed UI test. Each file looks small on its own, but pipelines run all day. After a few months, that pile turns into a storage problem.
Feature branches usually create the quietest waste. A developer opens a branch, pushes ten times, gets review comments, pushes a few more times, then merges it and moves on. If every run keeps artifacts forever, GitLab stores a trail of old files nobody will open again.
The problem grows fast on busy teams. Ten developers can create hundreds of pipeline runs in a week, and many of those runs produce nearly identical artifacts. You end up paying to keep copies of work that lost its use as soon as a newer commit passed.
That hits the budget in a very direct way. More storage means higher GitLab storage costs, and most of that money buys nothing useful. Teams rarely download artifacts from stale feature branches, old bugfix attempts, or failed experiments from three months ago.
It also makes everyday work messier. When someone actually needs a file, they have to dig through a long list of old jobs. The right artifact may still exist, but it sits next to dozens of outdated builds with similar names.
Most storage clutter comes from repeated artifacts on active feature branches, old merge request pipelines, failed job outputs kept long after the issue was fixed, and release candidate files nobody promoted. The result is more than a bigger bill. Teams waste time guessing which artifact still matters, and cleanup turns into a manual chore nobody wants.
A quick check usually makes the problem obvious. Open a project with heavy CI use and count how many artifacts belong to branches that no longer exist. That number alone often explains why storage keeps growing.
Which artifacts should stay and which should go
Most teams keep too much because every artifact feels useful on the day it appears. A week later, half of those files have no job left. The easiest way to set sane retention rules is to sort artifacts by purpose, not by habit.
Short-lived test outputs should expire quickly. That includes temporary build logs, preview bundles, test reports for feature branches, and files created only to move one merge request forward. Once the review ends or the branch closes, those files usually have no reason to stay.
Release files are different. Keep anything you may need to ship again, inspect during an incident, or use for a rollback. That often includes production build packages, release archives, migration bundles, and the small set of reports your team needs for audits or customer questions. If losing a file would slow down recovery or force you to rebuild under pressure, keep it longer.
A simple rule works well: if a file supports one conversation, drop it soon. If it supports recovery, compliance, or a real customer release, keep it.
Most teams do not need a complicated classification system. Four labels are usually enough: review-only, branch build, release, and audit or rollback. With labels like that, storage decisions stop feeling emotional. A developer can look at an artifact and know why it exists, how long it should live, and who would miss it if it disappeared.
One example makes this clearer. A feature branch creates a test bundle, screenshots, and a coverage report for one merge request. Those files help reviewers today, but almost never next month. A tagged release creates an install package and release notes tied to a customer deployment. That set should stay because the team may need to compare versions or roll back.
If your team argues over edge cases, use a blunt test: would anyone open this file after the current review cycle ends? If the honest answer is no, set a short expiry. That habit cuts storage creep faster than most cleanup scripts.
Sort branches and releases into simple groups
Retention gets much easier when you stop making exceptions for every branch. GitLab works best here when you sort builds into a few predictable buckets.
A good starting point is four groups: feature branches, main, release branches, and tagged releases. Hotfixes can fit into the same model with one extra rule instead of a custom policy every time someone rushes a patch out.
Use four buckets, not twenty
Feature branches belong in a short-retention bucket. These builds help during review and testing, but they lose value fast after a merge or an abandoned branch. For many teams, 3 to 7 days is enough.
Main branch builds deserve a longer window. People often go back to them to compare behavior, test a rollback, or inspect a recent package. Keeping them for 14 to 30 days is usually plenty.
Release branches are different from tagged releases, and teams often mix them up. A release branch still moves. You may patch it, rebuild it, or test it several times before the final ship date. That makes it closer to ongoing work than to a permanent record.
Tagged releases are the builds you may need months later. They match a shipped version, so they should stay much longer than a release branch build. Some teams keep them for 6 to 12 months. Others keep only the latest few tagged releases because they publish often.
Keep hotfix rules boring
Hotfix builds create chaos when every urgent fix gets a one-off exception. Skip that. Write one rule and use it every time.
A plain rule works well: keep hotfix branch artifacts for the same window as release branches, and keep tagged hotfix releases for the same window as other tagged releases. That gives you enough time to verify the fix, compare files, and roll back if needed.
For a small startup team, this kind of grouping is easier to manage than branch-specific settings. People can remember it, and that matters. A policy only helps when the team actually follows it.
Set expiry windows step by step
Start with a plain inventory. Write down the branch types your team actually uses, not the ones you think you use. Most teams only need a short list: feature branches, bugfix branches, main, release branches, and tagged releases.
Then map your release flow. Ask two simple questions: which builds do people need again, and for how long? A feature branch artifact often loses value in days. A tagged production release may need to stay for months because support, rollback, or audits depend on it.
You do not need anything fancy to decide on first-pass windows. For many teams, a good starting point looks like this:
- Feature and bugfix branches: 3 to 7 days
- Main branch: 14 to 30 days
- Release branches: 30 to 90 days
- Tagged releases: 6 to 12 months
That is enough to start a branch retention policy that cuts waste without causing panic.
After that, add the rules to your CI jobs in small batches. Do not change every pipeline on the same day. Pick one project with steady traffic and a predictable release cycle. Add expire_in values to the jobs that produce the biggest artifacts first, such as build packages, test bundles, and compiled outputs.
If your team uses different jobs for branch builds and release builds, keep that logic separate. The pipeline will be easier to read later, and people will make fewer mistakes when they update it.
Watch that test project for one or two release cycles. Check whether anyone tries to download an artifact after it expires. If nobody notices, your window is probably safe. If people complain, extend only the group that caused trouble. Do not raise every window because one case came up.
Before you roll the policy out wider, decide who can approve exceptions. Keep that list short. Usually one engineering lead and one release owner are enough. If everyone can mark builds for long retention, the policy stops working fast.
Good retention rules are boring. A few clear windows, a small pilot, and named approvers usually do more for storage costs than a long list of special cases.
A simple policy example for one team
A six-person product team can keep storage under control with a few clear rules. They ship small changes most days, cut a release every two weeks, and push an urgent fix now and then. That pace does not need endless artifact history.
For daily work, they sort branches by how long the build will stay useful. Most feature branches live for only a few days, then the merge request closes and nobody touches the artifacts again. So the team keeps those files for 5 days, and stretches to 7 only when reviews run long.
Their policy is simple:
- Feature branches: 3 to 7 days
- Main branch: 14 to 30 days
- Release branches: keep artifacts until the release ships
- Tagged production releases: keep them for months, or longer if compliance requires it
- Hotfix builds: use the same rule as tagged production releases
The main branch gets a longer window because bugs often show up after a merge, not before. If a problem appears in this week's build, the team can still pull a recent artifact, compare outputs, and check what changed without rebuilding old commits. Around 21 days is a good middle ground for many teams.
Release branches stay alive until the release goes out. During that period, QA may rerun tests, product may ask for one last check, and developers may need the exact same build again. Deleting those artifacts too early creates busywork and slows the release.
Tagged production releases and hotfixes stay much longer because they are part of the record. If a customer reports an issue three months later, the team can inspect the exact shipped build. In practice, this split works better than one expiry value for every branch.
This policy is boring on purpose. It cuts waste without making debugging harder. If your team ships slower, keep main branch artifacts closer to 30 days. If your team ships fast and merges often, keep feature branch artifacts closer to 3 days.
Mistakes that waste storage
A lot of teams set one generous expiry for every artifact and call it done. That feels safe, but it quietly grows your bill. A feature branch build from last Tuesday does not need the same lifetime as a tagged release that support may need to inspect months later.
The usual problem is simple: nobody sorts artifacts by purpose. Review builds, quick test outputs, and release packages end up on the same long timer. When retention rules do not match how people actually work, storage keeps climbing even when the pipeline looks tidy.
Retried jobs add another layer of waste. A flaky test or a failed deployment can leave several copies of similar files behind, especially when teams rerun pipelines a few times to get a green check. One noisy branch can produce far more stored data than most people expect.
Large test bundles are another common leak. Teams often save full browser screenshots, raw logs, coverage reports, and exported test data for every run. That can make sense for a short window, but most of those files never get opened after the first day or two. If nobody checks them later, they should expire fast.
Where teams mix things up
Release evidence needs a different rule from temporary review artifacts. Signed packages, compliance records, and files tied to a shipped version may need a longer life. Preview builds for QA, merge request screenshots, and throwaway debug bundles usually do not. When both groups share one policy, you either keep junk too long or delete release files too early.
Teams also run into trouble when they change retention without warning developers and QA. A tester expects a build to still exist on Friday, but the new rule removed it on Wednesday. Then people work around the problem by uploading files somewhere else, and the process gets messy fast.
A short sanity check helps. Keep branch artifacts on short timers, limit retries that store the same outputs again, save heavy test bundles only when someone will review them, separate release artifacts from review and debug files, and tell developers and QA before rules change. It sounds basic, but it saves real money.
Quick checks before you turn rules on
Retention rules save money fast, but a bad cutoff can break a rollback or wipe out the one build a developer still needs. Check your real recovery and support habits first, not the policy you wish you had.
Start with releases. Pick one recent production version and ask: if it fails tonight, can the team redeploy it without rebuilding from source? Some teams keep only source tags and assume they can recreate everything. That works until a dependency changes, a package disappears, or the build environment no longer matches. If rollback depends on stored artifacts, keep release artifacts longer than branch builds.
Audits need the same reality check. Engineering may be happy with 14 or 30 days, but finance, security, or a customer contract may expect release evidence to stick around much longer. Confirm that with the person who owns compliance. Guessing here is how teams end up restoring old backups just to answer a simple question.
Now look at size, not just age. In most GitLab projects, a few jobs create most of the storage bill. Package builds that bundle large binaries, UI test runs that save videos and screenshots, scan jobs that archive full reports, and failed debug jobs that upload entire workspaces are usually the main culprits.
Those heavy jobs deserve a closer look before you apply new rules. A 2 GB artifact kept for seven days is a very different problem from a 30 MB test report kept for 30 days.
Failed pipelines need special attention too. They often store more than successful runs because developers add extra logs, screenshots, crash dumps, and temporary files to help diagnose problems. That makes sense in the moment, but those files pile up fast. Keep enough failed pipeline history to debug recent issues, then cut the rest.
Last, ask developers how far back they actually go when they investigate a bug. Many teams only need recent branch builds for a week or two, while release candidates and production tags stay much longer. If people still debug issues from last month's main branch build, keep that window. If they never do, do not pay to store it.
How to watch storage after the change
Take a snapshot of storage totals on the day you change expiry rules. Then check the same numbers every week for at least a month. One before-and-after screenshot is not enough. You want a short trend line that shows whether artifact storage is flattening or still climbing.
Track artifact storage separately from the full project total when you can. Total project size can move for other reasons, such as container images, packages, or repository growth. That can hide whether your new expiry windows are working.
What to measure each week
Keep the record simple and consistent. Track total project storage, artifact storage, the three jobs that create the largest files, and any complaints about missing artifacts.
That third item matters more than most teams expect. Storage problems usually come from a few noisy jobs, not every pipeline. A browser test job that saves videos, screenshots, and logs for every branch can eat more space than dozens of small build jobs.
Look at those heavy jobs first. If one job creates a 1.5 GB artifact and nobody opens it after the first day, shorten its expiry again. If another job makes a smaller file but developers reopen it every week to debug release issues, keep it longer.
That part needs actual feedback from the team. Ask developers which artifacts they reopen, how often they need them, and what they do when the files are gone. Keep the question simple.
Next steps for a leaner GitLab setup
Start small. Pick one repository with regular CI traffic, a few active branches, and enough artifact churn to show clear results within a week or two. A simple test beats a grand cleanup plan that touches everything at once.
Set one policy, watch it, and adjust it quickly. Keep release artifacts longer, keep default branch artifacts for a useful review window, and let short-lived feature branch builds expire much sooner. That is usually enough to prove whether your retention rules match real team habits.
A practical rollout is simple: apply the policy to one repository, check storage use and developer complaints, copy the same structure to similar projects, and keep a short note for each exception.
When the first repository settles down, reuse the same pattern across other projects. Do not rewrite the policy from scratch every time. Most teams only need a small set of buckets, such as release, main branch, support branch, and temporary feature work.
Write down exceptions while they are still fresh. Some teams must keep release builds longer because of support promises, audits, or signed deliverables. Others need a longer window for regulated work or for clients who ask for exact build traceability months later. If you leave those cases undocumented, people will work around the policy and storage will creep up again.
Complex GitLab setups need more care. Shared runners, parent-child pipelines, many teams in one group, and mixed release habits can create edge cases that do not show up in a small test repository.
If your team wants a second pair of eyes, a short review can help. Oleg Sotnikov at oleg.is works with startups and smaller companies on infrastructure, CI, and cost control, so this kind of cleanup usually does not need to turn into a big project.
The best next move is usually modest: fix one repository well, copy the pattern carefully, and keep exceptions on paper instead of in people's heads.
Frequently Asked Questions
What artifact expiry should I start with in GitLab?
A safe starting point is 3 to 7 days for feature and bugfix branches, 14 to 30 days for main, 30 to 90 days for release branches, and 6 to 12 months for tagged releases.
Run that for one or two release cycles, then change only the group that causes real friction.
Should feature branch artifacts stay around for a long time?
Usually no. Feature branch artifacts lose value fast after a merge, a closed merge request, or an abandoned branch.
Keep them a bit longer only when reviews move slowly or QA still checks that branch.
What is the difference between a release branch and a tagged release for retention?
A release branch still changes. Teams patch it, rebuild it, and test it before they ship, so it needs a medium retention window.
A tagged release matches a shipped version. Keep that much longer because support, rollback, or customer questions may depend on the exact files.
How should I handle hotfix artifacts?
Keep the rule boring. Store hotfix branch artifacts for the same window as release branches, and store tagged hotfix releases for the same window as other tagged releases.
That gives your team enough time to verify the fix and roll back if needed without creating one-off exceptions.
Which artifacts should I keep the longest?
Keep the files you may need under pressure. That usually means production build packages, migration bundles, release notes tied to a shipped version, and audit or rollback evidence.
Drop files that only support one review or one short test cycle.
Which CI jobs usually waste the most GitLab storage?
UI test jobs often top the list because they save videos, screenshots, and logs for every run. Large package builds, security scans with full report archives, failed debug jobs, and retried pipelines also eat space fast.
Check those jobs first before you tune smaller reports.
How do I know if my retention window is too short?
Run a small pilot and watch real behavior. If developers or QA try to download artifacts after they expire during the next release cycle, your window is too short for that group.
Extend only that group instead of raising every expiry across the project.
Should I keep artifacts from failed pipelines?
Yes, but keep the window short. Failed runs often store extra logs, crash dumps, screenshots, and temporary files, so they grow faster than successful ones.
Keep enough history to debug recent problems, then let older failed artifacts expire.
What is the safest way to roll out new retention rules?
Pick one busy repository first. Add expiry to the largest artifact jobs, tell developers and QA before the change, and watch storage plus missing artifact complaints for a few weeks.
Also keep exception approvals with one engineering lead and one release owner so the policy does not drift.
Can I lower storage costs without making rollbacks harder?
Yes. Split branch artifacts from release artifacts and treat them differently.
Before you shorten anything, try redeploying one recent production version without rebuilding it. If rollback depends on stored artifacts, keep tagged releases longer and cut branch builds instead.