How does GitLab's Container Registry work and how do you integrate it into CI/CD pipelines for building, pushing, and deploying Docker images?
Quick Answer
GitLab Container Registry is a built-in Docker registry integrated into each project. Images are stored at registry.gitlab.com/group/project and can be pushed during CI/CD using the predefined CI_REGISTRY variables. It supports image expiration policies, vulnerability scanning, and seamless authentication via CI job tokens.
Detailed Answer
Think of GitLab Container Registry like a company warehouse attached directly to the factory floor. Instead of shipping your products (Docker images) to a third-party storage facility (Docker Hub, ECR), you store them right next to the assembly line (CI/CD pipeline). Workers (Runners) have automatic badge access (CI_JOB_TOKEN) to the warehouse, and every department (project) has its own labeled shelving section (image repository).
GitLab Container Registry is an integrated Docker registry that comes bundled with GitLab. Every project has its own registry namespace at registry.gitlab.com/<namespace>/<project>. Images can have multiple tags and follow the standard OCI image specification. The registry is fully integrated with GitLab's authentication system: users authenticate with their personal access tokens or deploy tokens, and CI/CD jobs authenticate automatically using the CI_JOB_TOKEN predefined variable. The registry supports both Docker manifest v2 and OCI image manifests, meaning it works with Docker, Podman, Buildah, and any OCI-compliant tool. Each project's registry is accessible from the project sidebar under Packages & Registries > Container Registry, where you can browse images, view tags, see image sizes, and delete tags manually.
Internally, GitLab Container Registry runs as a separate service (based on the Docker Distribution project) that shares authentication with the GitLab Rails application. When a docker push command is executed, the Docker client first contacts GitLab for an authentication token, then pushes image layers to the registry storage backend. GitLab supports local filesystem, S3, GCS, and Azure Blob Storage as storage backends for the registry. Each image tag is a manifest pointing to a set of layers (blobs). The garbage collection process removes unreferenced blobs to reclaim storage space. For CI/CD integration, GitLab provides several predefined variables: CI_REGISTRY (the registry URL), CI_REGISTRY_IMAGE (the project's image path), CI_REGISTRY_USER (set to gitlab-ci-token), and CI_REGISTRY_PASSWORD (the job token). These variables enable seamless authentication without hardcoding credentials.
In production, a team building notification-service would configure their pipeline to build and push images on every commit to main and on merge request branches. The build job uses Docker-in-Docker (dind) or Kaniko (a rootless alternative) to build the image, tags it with the commit SHA for immutability and also tags main builds as latest. The deployment job pulls the image from the registry using the commit SHA tag, ensuring exactly the built version is deployed. Teams configure cleanup policies to automatically delete tags older than 90 days or tags matching patterns like branch-* that were created for MR builds. For multi-architecture support, the pipeline uses docker buildx to create manifest lists containing both AMD64 and ARM64 images under a single tag, enabling deployment to mixed-architecture clusters.
A critical gotcha is running out of storage because old image tags are never cleaned up. Without a cleanup policy, every pipeline push adds image layers that accumulate indefinitely. Configure the Container Registry cleanup policy under Settings > Packages & Registries > Clean up image tags, setting rules like keep the 5 most recent tags and delete tags older than 14 days matching the regex feature-.*. Another common mistake is using Docker-in-Docker with the docker executor without enabling TLS or using the --privileged flag, which causes build failures. Using Kaniko avoids this issue entirely since it builds images in userspace without requiring Docker daemon access.