GitLab Container Registry

The GitLab Container Registry is a built-in storage location for Docker images. Instead of pushing images to Docker Hub or a separate registry, you store them directly inside GitLab alongside your code, pipelines, and issues.

What a Container Image Is

A container image is a packaged snapshot of your application and everything it needs to run — the code, runtime, libraries, and settings — bundled into one portable file. Run the image anywhere using Docker and your app works identically, regardless of the host machine.

  Traditional deployment:                  Container deployment:
  ─────────────────────────                ──────────────────────────────
  App needs: Node 18, PostgreSQL 14        App packaged in one image
  Server A has: Node 16 → breaks ❌        Server A runs image → works ✅
  Server B has: Node 18 → works ✅         Server B runs image → works ✅
  Server C has: Node 20 → breaks ❌        Server C runs image → works ✅

Enabling the Container Registry

On GitLab.com, the Container Registry is enabled by default for every project. Access it at Deploy → Container Registry in your project sidebar. Self-hosted GitLab instances may need an admin to enable it at the instance level first.

The Dockerfile — Blueprint for Your Image

A Dockerfile is a text file with instructions that Docker reads to build an image. Place it in the root of your repository.

  Dockerfile
  ─────────────────────────────────────────────
  FROM node:20-alpine          ← start from official Node.js image

  WORKDIR /app                 ← set the working directory

  COPY package*.json ./        ← copy package files first
  RUN npm ci                   ← install dependencies

  COPY . .                     ← copy all other source files

  RUN npm run build            ← build the production app

  EXPOSE 3000                  ← the app listens on port 3000

  CMD ["node", "dist/server.js"]  ← command to start the app

Building and Pushing an Image From Your Terminal

Step 1 — Log In to the Registry

  docker login registry.gitlab.com -u YOUR_USERNAME -p YOUR_TOKEN

Use a Personal Access Token (not your password) with the read_registry and write_registry scopes. Create one at Preferences → Access Tokens.

Step 2 — Build the Image

  docker build -t registry.gitlab.com/acme/my-webapp:latest .

The image tag format is always: registry.gitlab.com/NAMESPACE/PROJECT:TAG

Step 3 — Push the Image

  docker push registry.gitlab.com/acme/my-webapp:latest

The image now appears in Deploy → Container Registry inside your GitLab project.

Automating Image Builds in CI/CD

The most common pattern is to build and push images automatically in your pipeline whenever code is merged to main.

  stages:
    - test
    - build-image

  run-tests:
    stage: test
    image: node:20
    script:
      - npm test

  build-and-push:
    stage: build-image
    image: docker:24
    services:
      - docker:24-dind          ← Docker-in-Docker service
    variables:
      IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
    script:
      - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
      - docker build -t $IMAGE:$CI_COMMIT_SHORT_SHA .
      - docker build -t $IMAGE:latest .
      - docker push $IMAGE:$CI_COMMIT_SHORT_SHA
      - docker push $IMAGE:latest
    only:
      - main

GitLab automatically provides $CI_REGISTRY_USER, $CI_REGISTRY_PASSWORD, and $CI_REGISTRY — no manual secret setup needed for your own project's registry.

Image Tags — Labelling Versions

Tags identify different versions of the same image. Using clear tags makes it easy to roll back to a previous version if a new release causes issues.

  registry.gitlab.com/acme/my-webapp:latest        ← most recent build
  registry.gitlab.com/acme/my-webapp:a1b2c3d       ← specific commit
  registry.gitlab.com/acme/my-webapp:v2.1.0        ← release version
  registry.gitlab.com/acme/my-webapp:staging       ← staging environment

Pulling an Image for Deployment

On any server that has Docker installed and access to your registry:

  docker login registry.gitlab.com -u USERNAME -p TOKEN

  docker pull registry.gitlab.com/acme/my-webapp:v2.1.0

  docker run -d -p 3000:3000 registry.gitlab.com/acme/my-webapp:v2.1.0

Registry Storage and Cleanup

Every image push consumes storage. Old images accumulate quickly. Set up cleanup policies to delete images automatically.

Go to Settings → Packages and registries → Container registry → Cleanup policy. Configure rules like:

  Cleanup policy example:
  ─────────────────────────────────────────────────────────────
  Run cleanup:        Every week
  Remove tags older than:  90 days
  Keep the most recent:    10 tags
  Exclude tags matching:   latest, v*.*.*  (keep release tags)
  ─────────────────────────────────────────────────────────────

Viewing Images in the Registry

Go to Deploy → Container Registry. You see each image repository, its tags, the digest (a unique fingerprint), size, and when it was last pushed.

  Container Registry — acme/my-webapp
  ──────────────────────────────────────────────────────────────────
  Tag              Digest        Size      Pushed
  ──────────────────────────────────────────────────────────────────
  latest           sha256:ab12   245 MB    2 hours ago
  a1b2c3d          sha256:ab12   245 MB    2 hours ago
  v2.0.0           sha256:cd34   238 MB    1 week ago
  v1.9.5           sha256:ef56   231 MB    3 weeks ago
  ──────────────────────────────────────────────────────────────────

Using Registry Images as CI/CD Job Images

You can use your own registry images as the base image for CI/CD jobs. This is useful when you build a custom image with all your tools pre-installed, speeding up job startup time.

  Custom base image: registry.gitlab.com/acme/ci-tools:latest
  (contains node, python, kubectl, and aws-cli pre-installed)

  run-tests:
    image: registry.gitlab.com/acme/ci-tools:latest
    script:
      - npm test
      - python -m pytest

Jobs start in seconds because all tools are already in the image instead of being installed fresh each run.

Leave a Comment

Your email address will not be published. Required fields are marked *