Controlling GitLab CI Artifact Retention with expire_in
Learn how to control GitLab CI artifact retention with the artifacts: expire_in keyword, see a concrete .gitlab-ci.yml example, and verify expiration via the UI and API.
12 Apr 2026, 15:09 UTC

Problem: Artifacts piling up and eating storage
When a GitLab CI pipeline runs, each job can upload build outputs, test reports, or Docker images as artifacts. If no retention policy is set, GitLab keeps these files forever. Over time, especially in active projects, the stored artifacts can consume gigabytes of disk space, increase backup times, and raise costs for self‑managed instances.
Solution: Use the artifacts: expire_in keyword
GitLab CI lets you define how long an artifact should be stored before it is automatically removed. The expire_in value can be a human‑readable string like "30 days" or a numeric number of seconds. When the deadline passes, GitLab marks the artifact as expired and later purges it during its regular cleanup cycle.
How expire_in works
The expiration timestamp is calculated from the moment the job finishes successfully. GitLab stores this timestamp internally and shows it in the UI under the job’s "Artifacts" section. The same value is exposed via the REST API as the expire_at field.
Global default vs per‑job override
You can set a project‑wide default expiration in Settings → CI/CD → Artifact expiration. This default applies to every job that does not explicitly define expire_in. Adding artifacts: expire_in inside a job’s definition overrides the project default for that job only, allowing fine‑grained control—for example, keeping build binaries for a week while preserving test reports for a month.
Worked example: Per‑job expiration in .gitlab-ci.yml
Place the following file at the root of your repository and push it to the default branch. You need at least the Maintainer role to push to protected branches.
# .gitlab-ci.yml
stages:
- build
- test
build_job:
stage: build
script:
- echo "Compiling application"
- mkdir -p dist && echo "dummy binary" > dist/app
artifacts:
paths:
- dist/
expire_in: 7 days # keep build output for one week
test_job:
stage: test
script:
- echo "Running tests"
- mkdir -p reports && echo "passed" > reports/junit.xml
artifacts:
paths:
- reports/
reports:
junit: reports/junit.xml
expire_in: 14 days # keep test reports for two weeks
After the pipeline runs, navigate to CI/CD → Jobs, select a job, and click the "Artifacts" button. The UI will show an expiration date (e.g., "Expires in 6 days").
Verifying expiration via the API
You can confirm the timestamp programmatically. Replace :id with your project’s numeric ID and :job_id with the job’s ID from the UI.
GET https://gitlab.example.com/api/v4/projects/:id/jobs/:job_id/artifacts
Private-Token:
The JSON response includes an expire_at field in ISO 8601 format. Compare this value to the current time; if the difference matches the expire_in you set, the configuration is working.
Limitations and practical checks
- Expiration only affects artifacts stored in GitLab’s built‑in storage. If you use an external artifact repository (e.g., Nexus, S3) via the
dependenciesorcachekeywords, those files are not touched byexpire_in. - Changing the project default or a job’s
expire_indoes not retroactively expire existing artifacts. To clean up old files you must either wait for the original deadline to pass or manually delete them via the UI or API. - Very short expiration values (e.g., "1 minute") may cause artifacts to disappear before you can download them for debugging. Choose a window that balances storage savings with usability.
To verify that expiration is taking effect, monitor your instance’s storage usage over time (Admin → Overview → Storage) or run a periodic API job that lists artifacts with expire_at in the past and confirms they are no longer downloadable.
Actionable closing
Review your current .gitlab-ci.yml files. Identify jobs that produce large, short‑lived outputs and add an appropriate expire_in value. Start with a conservative period (e.g., 7 days for binaries, 30 days for reports) and adjust after observing storage trends. By explicitly defining artifact lifespans you keep your GitLab instance tidy without sacrificing the ability to retrieve needed build evidence.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.