# GitLab Container Registry Reference ## Overview GitLab Container Registry is a secure Docker image registry built into GitLab, allowing you to store and manage Docker images. ## Registry URL Format ``` registry.gitlab.com/namespace/project ``` For self-hosted: ``` registry.your-domain.com/namespace/project ``` ## Authentication ### Docker Login ```bash # Using personal access token docker login registry.gitlab.com -u -p # Using deploy token docker login registry.gitlab.com -u -p # In CI/CD (automatic) docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY ``` ### Token Scopes Personal Access Token needs: - `read_registry` - Pull images - `write_registry` - Push images Deploy Token needs: - `read_registry` - Pull images - `write_registry` - Push images ## Building and Pushing Images ### Basic Docker Build ```yaml # .gitlab-ci.yml build: stage: build image: docker:latest services: - docker:dind variables: DOCKER_TLS_CERTDIR: "/certs" before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA ``` ### Multi-Stage Build **Dockerfile**: ```dockerfile # Build stage FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage FROM node:18-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY package*.json ./ RUN npm ci --production EXPOSE 3000 CMD ["node", "dist/server.js"] ``` ### Tagging Strategy ```yaml build: script: # Commit SHA tag - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA # Branch name tag - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG # Latest tag (main branch only) - | if [ "$CI_COMMIT_BRANCH" == "main" ]; then docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest docker push $CI_REGISTRY_IMAGE:latest fi # Version tag (for tags) - | if [ -n "$CI_COMMIT_TAG" ]; then docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG fi ``` ## Pulling Images ### Pull from Registry ```bash # Pull specific tag docker pull registry.gitlab.com/group/project:tag # Pull latest docker pull registry.gitlab.com/group/project:latest # Pull by SHA docker pull registry.gitlab.com/group/project@sha256:abc123... ``` ### Use in CI/CD ```yaml test: image: $CI_REGISTRY_IMAGE:latest script: - npm test ``` ### Use in Docker Compose ```yaml version: '3.8' services: app: image: registry.gitlab.com/group/project:latest ports: - "3000:3000" environment: - NODE_ENV=production ``` ## Registry Management ### List Repository Tags ```bash curl --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories/:repository_id/tags" ``` ### Get Tag Details ```bash curl --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories/:repository_id/tags/:tag_name" ``` ### Delete Tag ```bash curl --request DELETE --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories/:repository_id/tags/:tag_name" ``` ### Delete Tags in Bulk ```bash # Delete by regex curl --request DELETE --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories/:repository_id/tags" \ --data "name_regex=.*-dev" \ --data "keep_n=5" \ --data "older_than=7d" ``` ## Cleanup Policies ### Configure Cleanup Policy **Via UI**: 1. Project Settings > Packages & Registries > Container Registry 2. Configure cleanup policy 3. Set rules **Via API**: ```bash curl --request PUT --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories/:repository_id" \ --data "cadence=1d" \ --data "keep_n=10" \ --data "older_than=30d" \ --data "name_regex=.*-dev" \ --data "name_regex_keep=.*-stable" ``` **Policy options**: - `cadence`: How often to run (1d, 7d, 14d, 1month, 3month) - `keep_n`: Keep N most recent tags - `older_than`: Delete tags older than specified time - `name_regex`: Regex for tags to delete - `name_regex_keep`: Regex for tags to keep ### Cleanup Example ```yaml # Keep production tags forever # Delete dev/feature tags after 7 days # Keep last 5 tags per branch cleanup_policy: enabled: true cadence: 1d keep_n: 5 older_than: 7d name_regex: '^(?!main|prod|release).*' name_regex_keep: '^(main|prod|release-.*|v\d+\.\d+\.\d+)$' ``` ## Registry Access Control ### Project-Level Access Members inherit registry permissions from project role: - Guest: No access - Reporter: Pull images - Developer: Pull and push images - Maintainer: Full access - Owner: Full access ### Deploy Tokens Create deploy tokens for automation: ```bash curl --request POST --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/deploy_tokens" \ --data "name=Registry Token" \ --data "scopes[]=read_registry" \ --data "scopes[]=write_registry" \ --data "expires_at=2025-12-31" ``` Use in CI/CD: ```yaml variables: REGISTRY_TOKEN_USER: "deploy-token-user" REGISTRY_TOKEN_PASS: "deploy-token-password" deploy: script: - docker login -u $REGISTRY_TOKEN_USER -p $REGISTRY_TOKEN_PASS $CI_REGISTRY - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA ``` ## Building with Kaniko Alternative to Docker-in-Docker: ```yaml build: stage: build image: name: gcr.io/kaniko-project/executor:debug entrypoint: [""] script: - mkdir -p /kaniko/.docker - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json - /kaniko/executor --context "${CI_PROJECT_DIR}" --dockerfile "${CI_PROJECT_DIR}/Dockerfile" --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}" --cache=true --cache-ttl=24h ``` **Advantages**: - No Docker daemon needed - More secure (no privileged mode) - Better for Kubernetes - Built-in caching ## Building with Buildah Daemonless container builds: ```yaml build: stage: build image: quay.io/buildah/stable script: - buildah login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - buildah bud -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - buildah push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA ``` ## Multi-Platform Images ### Build for Multiple Architectures ```yaml build-multiarch: stage: build image: docker:latest services: - docker:dind before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - docker buildx create --use script: - docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --tag $CI_REGISTRY_IMAGE:latest --push . ``` ## Image Scanning ### Container Scanning ```yaml include: - template: Security/Container-Scanning.gitlab-ci.yml variables: CS_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA container_scanning: dependencies: - build ``` ### Trivy Scanner ```yaml trivy_scan: stage: test image: name: aquasec/trivy:latest entrypoint: [""] script: - trivy image --exit-code 0 --no-progress $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA - trivy image --exit-code 1 --severity CRITICAL --no-progress $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA ``` ## Image Signing ### Sign with Cosign ```yaml sign: stage: sign image: gcr.io/projectsigstore/cosign:latest script: - echo "$COSIGN_KEY" > cosign.key - cosign sign --key cosign.key $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA ``` ### Verify Signature ```bash cosign verify --key cosign.pub registry.gitlab.com/group/project:tag ``` ## Registry Storage ### Configure Storage Backend **Local storage**: ```ruby # /etc/gitlab/gitlab.rb registry['storage'] = { 'filesystem' => { 'rootdirectory' => '/var/opt/gitlab/gitlab-rails/shared/registry' } } ``` **S3 storage**: ```ruby registry['storage'] = { 's3' => { 'accesskey' => 'AKIAIOSFODNN7EXAMPLE', 'secretkey' => 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', 'bucket' => 'gitlab-registry', 'region' => 'us-east-1' } } ``` **GCS storage**: ```ruby registry['storage'] = { 'gcs' => { 'bucket' => 'gitlab-registry', 'keyfile' => '/path/to/keyfile.json' } } ``` **Azure storage**: ```ruby registry['storage'] = { 'azure' => { 'accountname' => 'accountname', 'accountkey' => 'base64encodedaccountkey', 'container' => 'gitlab-registry' } } ``` ## Registry Mirroring ### Pull from External Registry ```yaml variables: DOCKER_AUTH_CONFIG: | { "auths": { "docker.io": { "auth": "$(echo -n $DOCKERHUB_USER:$DOCKERHUB_PASSWORD | base64)" } } } build: script: # Pull from Docker Hub - docker pull nginx:alpine # Tag for GitLab registry - docker tag nginx:alpine $CI_REGISTRY_IMAGE/nginx:alpine # Push to GitLab registry - docker push $CI_REGISTRY_IMAGE/nginx:alpine ``` ## Troubleshooting ### Common Issues **1. Authentication Failed** ```bash # Clear Docker credentials rm ~/.docker/config.json # Re-authenticate docker login registry.gitlab.com ``` **2. Push Denied** ```bash # Check token permissions # Ensure token has write_registry scope # Verify project access level ``` **3. Image Not Found** ```bash # Verify image path docker pull registry.gitlab.com/namespace/project:tag # Check tag exists curl --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories" ``` **4. Storage Issues** ```bash # Check disk space df -h /var/opt/gitlab # Run garbage collection gitlab-rake gitlab:cleanup:container_registry ``` ### Enable Debug Logging ```ruby # /etc/gitlab/gitlab.rb registry['log_level'] = 'debug' registry['log_formatter'] = 'json' ``` ## Best Practices ### 1. Image Size Optimization ```dockerfile # Use minimal base images FROM alpine:latest # Multi-stage builds FROM node:18 AS builder # ... build steps ... FROM node:18-alpine COPY --from=builder /app/dist ./dist # Remove unnecessary files RUN rm -rf /tmp/* /var/cache/apk/* # Combine RUN commands RUN apk add --no-cache curl && \ curl -o file.tar.gz https://example.com/file.tar.gz && \ tar -xzf file.tar.gz && \ rm file.tar.gz ``` ### 2. Security - Scan all images - Use specific tags (not latest) - Sign images - Regularly update base images - Remove old images ### 3. Tagging - Use semantic versioning for releases - Include commit SHA for traceability - Tag by environment (dev, staging, prod) - Cleanup temporary tags ### 4. CI/CD Integration ```yaml stages: - build - test - scan - deploy build: stage: build script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA test: stage: test image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA script: - npm test scan: stage: scan variables: CS_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA include: - template: Security/Container-Scanning.gitlab-ci.yml deploy: stage: deploy script: - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest - docker push $CI_REGISTRY_IMAGE:latest only: - main ``` ## Registry API Reference ### List Repositories ```bash curl --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories" ``` ### Get Repository ```bash curl --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories/:repository_id" ``` ### Delete Repository ```bash curl --request DELETE --header "PRIVATE-TOKEN: " \ "https://gitlab.com/api/v4/projects/:id/registry/repositories/:repository_id" ``` ## Additional Resources - Container Registry Docs: https://docs.gitlab.com/ee/user/packages/container_registry/ - Docker Documentation: https://docs.docker.com/ - Cleanup Policies: https://docs.gitlab.com/ee/user/packages/container_registry/reduce_container_registry_storage.html