Controlling GitLab CI Artifact Expiration to Tame Storage Costs
GitLab CI artifacts accumulate by default, inflating repo size and slowing fetches. Learn how to set expiration via UI or YAML, handle version differences, and verify cleanup.
25 Jun 2026, 21:03 UTC

The default retention problem
If you've ever watched a git fetch stall while GitLab silently downloads megabytes of past job artifacts, you've felt the impact of uncontrolled CI retention. By default, GitLab keeps successful job artifacts for 30 days and failed job artifacts for 60 days. These timeboxes are designed to balance debugging access with repository hygiene, but teams working on large codebases or long-lived branches often find that 30 days isn't enough, and 60 days leaves too much clutter. Artifacts accumulate not just in the project repository but also on runner machines, especially self‑managed runners that may not run the cleanup cycle frequently enough. The result: larger repository sizes, slower clone and fetch operations, and unexpected storage costs, especially on premium tiers where disk usage is billed.
Setting expiration via .gitlab-ci.yml
You can override the project‑wide defaults for any pipeline by adding an artifacts: expire_in key to your .gitlab-ci.yml. This accepts a relative time string such as 7d, 14d, 30d, or 0 to disable automatic cleanup. The configuration lives under the job level or globally under artifacts:, and it applies only to the artifacts uploaded by that job or pipeline.
code_job:
script: [...]
artifacts:
expire_in: 7d
paths:
- dist/
To add this, you need at least Maintainer permission for the project. The setting is stored in the pipeline metadata and takes effect as soon as the next pipeline runs. If you change expire_in after a pipeline has started, the in‑flight job continues with the old retention rule; only newly triggered pipelines respect the updated value.
Version‑aware considerations
GitLab 16 refreshed the CI/CD settings UI, moving the Artifact expiration field to a new location under Settings > CI/CD. If you're on GitLab 15 or earlier, the same artifacts: expire_in YAML key works, but the UI may not reflect it until you navigate the updated path. Self‑managed runners enforce expiration per runner instance; if a runner stops or is removed before the cleanup job runs, orphaned artifacts can persist. Additionally, changing expire_in does not retroactively clean up artifacts from pipelines that already completed—you'll need to trigger a manual cleanup or rely on the next scheduled garbage collection.
- GitLab 16 UI relocation: the Artifact expiration field moved under Settings > CI/CD.
- Self‑managed runner enforcement: orphaned artifacts may persist if runners retire before cleanup.
- Non‑retroactive changes: adjusting expire_in after pipelines finish won't affect existing artifacts.
Verify and adjust
To check that your expiration is active, navigate to Project > Settings > CI/CD and locate the Artifact expiration field; it should match the expire_in value you've set. After running a pipeline, visit the job's Artifacts page—the UI displays a Delete after timestamp consistent with your configuration. If you've set expire_in: 0, no automatic deletion occurs, and you'll need an external archival strategy or manual housekeeping. For most teams, a 7‑ to 14‑day expiration offers a practical balance between keeping recent build outputs accessible and preventing storage creep.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.