GitLab CI/CD: Understanding Artifacts, the Package Registry, and YAML Configuration
Introduction
GitLab’s integrated CI/CD platform provides powerful tools for automating builds, tests, and deployments. Two features that often cause confusion are Artifacts and the Package Registry. While both deal with files produced during a pipeline, they serve distinct purposes. This article explains the difference between them, walks through the basics of the Package Registry, clarifies the when: always keyword, and offers practical guidance on when and how to create your .gitlab-ci.yml file. By the end, you’ll know how to store temporary build outputs, publish reusable packages, and configure your pipelines with confidence.
1. Artifacts vs. Package Registry
| Aspect | Artifacts | Package Registry |
|---|---|---|
| Purpose | Temporary files generated by a pipeline (e.g., compiled binaries, test reports). | Permanent storage for reusable packages (Docker images, Maven, npm, PyPI, etc.). |
| Lifecycle | Usually retained for a limited time (default 7 days) and can be automatically expired. | Persist until you delete them; they act as a versioned dependency store. |
| Typical Use‑Cases | • Pass build output from one job to the next.• Provide downloadable logs or coverage reports.• Keep artifacts for debugging failed jobs. | • Host Docker images for deployment.• Publish internal npm modules.• Share Maven artifacts across micro‑services. |
| Access | Downloaded via the pipeline UI or via API using the job ID. | Pulled with standard package managers (docker pull, npm install, mvn dependency:get, etc.). |
| Configuration Location | Inside a job’s artifacts: block in .gitlab-ci.yml. |
Defined by enabling the appropriate Package Registry feature in the project settings and publishing from a job. |
Quick Example
# .gitlab-ci.yml
build:
stage: build
script:
- make compile
artifacts:
paths:
- build/*.jar # <-- temporary build output
expire_in: 1 day
publish:
stage: deploy
script:
- docker build -t registry.example.com/myapp:$CI_COMMIT_SHA .
- docker push registry.example.com/myapp:$CI_COMMIT_SHA
# No artifacts block – the Docker image lives in the Package Registry
In the snippet above, build creates a JAR file that is stored as an artifact for later jobs. The publish job pushes a Docker image to the Package Registry, where it can be pulled by any downstream environment.
2. What Is the GitLab Package Registry?
The GitLab Package Registry is a unified, self‑hosted repository for a variety of package formats:
- Docker (container images)
- Maven (Java libraries)
- npm (Node.js modules)
- RubyGems, Python (PyPI), Conan, Helm, and more
Key Benefits
- Single Source of Truth – Store all your binaries and libraries alongside the source code.
- Access Control – Leverage GitLab’s permission model to restrict who can publish or download packages.
- Dependency Management – Use standard tooling (
docker,mvn,npm) without external registries. - Traceability – Packages are linked to commits, tags, and pipeline IDs, making audits straightforward.
Publishing a Package (Docker Example)
# .gitlab-ci.yml
docker_build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
After the pipeline runs, the image appears under Packages & Registries → Container Registry in the project UI.
3. The when: always Keyword in Artifacts
Inside the artifacts: Block
test:
stage: test
script: npm test
artifacts:
when: always # <-- always upload artifacts
paths:
- coverage/
- Effect: The job will upload the specified
pathseven if the job fails. - Why use it? To retain logs, screenshots, or coverage reports that help debug failures.
Outside the artifacts: Block (Job‑Level when)
cleanup:
stage: cleanup
script: ./scripts/cleanup.sh
when: always # <-- job runs regardless of previous job status
- Effect: The job itself executes no matter whether earlier jobs succeeded or failed.
- Typical use‑case: Clean‑up steps, notifications, or publishing artifacts after a failure.
4. When to Create and Run .gitlab-ci.yml
-
Early Planning (Recommended)
- Add a minimal
.gitlab-ci.ymlat the start of the project to enable CI/CD from day one. - Example: a simple lint job that runs on every push.
- Add a minimal
-
Late Addition
- You can add the file later, but remember that pipelines only start after the file exists in the repository.
- If you need to avoid automatic deployments on the first run, use rules or only/except to limit execution.
-
Avoiding Unintended Deployments
deploy:
stage: deploy
script: ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual # <-- requires manual trigger
- The
rules:block ensures the deployment only runs manually on themainbranch, preventing accidental pushes from triggering a production rollout.
5. Practical Tips & Common Questions
Tips
- Set appropriate expiration for artifacts (
expire_in:) to keep storage costs low. - Version your packages using semantic versioning; GitLab automatically adds the version tag to the registry.
- Leverage caching (
cache:) for dependencies that don’t need to be stored as artifacts.
Common Questions
| Question | Answer |
|---|---|
| Can I download an artifact from a failed job? | Yes—use when: always in the artifacts: block to ensure it’s uploaded even on failure. |
| Do artifacts appear in the Package Registry? | No. Artifacts are pipeline‑specific; the Package Registry holds versioned packages meant for reuse across projects. |
| Do I need a separate registry for each project? | Not necessarily. You can push to a shared group-level registry or use the project’s own registry; permissions are inherited from the group. |
| How do I limit artifact size? | Use the paths: list to include only needed files and set expire_in: to control retention. |
Conclusion
Understanding the distinction between Artifacts (temporary pipeline outputs) and the Package Registry (persistent, versioned packages) is essential for building efficient GitLab CI/CD pipelines. Use when: always wisely to capture valuable debugging information, and decide early whether to add your .gitlab-ci.yml file to enable continuous integration from the start. With these concepts in hand, you can streamline builds, share reusable components, and maintain clean, maintainable pipelines across your organization.