How to Pull Docker Images from Private GitLab Registry with GitLab CI/CD

4 min read

When your .gitlab-ci.yml references a Docker image from a private GitLab Container Registry, the CI/CD runner needs authentication to pull it. How you set this up depends on whether the image is in the same project, a different project, or an external registry. This guide covers all three scenarios so you can pull Docker images from a private GitLab registry in your pipelines.

Prerequisites

  • A GitLab project with CI/CD enabled
  • A Docker image pushed to the GitLab Container Registry
  • Developer role or higher on the project (for CI_JOB_TOKEN access)

Method 1: Same-Project Images (Automatic)

If the Docker image is in the same project as your pipeline, GitLab handles authentication automatically using the CI_JOB_TOKEN. No extra configuration needed.

Reference the image using GitLab’s predefined variables:

build:
  image: $CI_REGISTRY_IMAGE:latest
  script:
    - echo "Running with private image"

$CI_REGISTRY_IMAGE expands to the full registry path for your project (e.g., registry.gitlab.com/your-group/your-project). The runner authenticates with CI_JOB_TOKEN behind the scenes.

You can also reference image tags and paths within the project:

image: $CI_REGISTRY_IMAGE/app:v2.1.0

Method 2: Cross-Project Images (Deploy Token)

If the image is in a different GitLab project, the job token won’t have access. Use a deploy token instead — it’s scoped to a project or group and doesn’t expire with jobs.

1. Create a deploy token

In the project that hosts the image:

  1. Go to Settings > Repository > Deploy tokens
  2. Set a name (e.g., ci-registry-read)
  3. Select the read_registry scope
  4. Click Create deploy token
  5. Copy the username and token — the token is shown only once

2. Encode the credentials

Base64-encode the username and token to create the auth string:

printf "%s:%s" "deploy-token-username" "deploy-token-value" | base64 -w0

This outputs a single base64 string like ZGVwbG95LXRva2VuLXVzZXJuYW1lOmRlcGxveS10b2tlbi12YWx1ZQ==.

3. Add DOCKER_AUTH_CONFIG as a CI/CD variable

In the project that runs the pipeline:

  1. Go to Settings > CI/CD > Variables
  2. Add a new variable with key DOCKER_AUTH_CONFIG
  3. Set the value to the following JSON, replacing the auth string with yours:
{
  "auths": {
    "registry.gitlab.com": {
      "auth": "ZGVwbG95LXRva2VuLXVzZXJuYW1lOmRlcGxveS10b2tlbi12YWx1ZQ=="
    }
  }
}
  • Set the variable type to Variable (not File)
  • Enable Mask variable to hide it in job logs
  • Optionally enable Protect variable to restrict it to protected branches

Now your pipeline can pull images from the other project’s registry:

build:
  image: registry.gitlab.com/other-group/other-project/app:latest
  script:
    - echo "Running with cross-project image"

Method 3: Docker Login in Docker-in-Docker Builds

If your pipeline uses Docker-in-Docker (DinD) to build or push images, you need to explicitly log in. Use CI_JOB_TOKEN for same-project access:

build-and-push:
  image: docker:latest
  services:
    - docker:dind
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_JOB_TOKEN $CI_REGISTRY
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
  • $CI_REGISTRY_USER — the username for registry authentication (set automatically by GitLab)
  • $CI_JOB_TOKEN — a short-lived token valid only for the duration of the job
  • $CI_REGISTRY — the registry URL (e.g., registry.gitlab.com)
  • $CI_COMMIT_SHORT_SHA — the short commit hash, used as the image tag

For cross-project pulls in DinD, replace the login credentials with a deploy token stored in CI/CD variables:

before_script:
  - docker login -u $DEPLOY_TOKEN_USER -p $DEPLOY_TOKEN $CI_REGISTRY

If your Docker builds hang during this process, see Fixing GitLab CI/CD Hangs: Building Docker Images for Lambda Runtime with MSSQL and ODBC for common causes.

Which Method Should You Use?

Scenario Method Auth Type
Image in the same project Method 1 CI_JOB_TOKEN (automatic)
Image in a different project or group Method 2 Deploy token + DOCKER_AUTH_CONFIG
Building/pushing images (Docker-in-Docker) Method 3 docker login with token

Avoid using Personal Access Tokens (PATs) in CI/CD pipelines. PATs are tied to a user account — if the user leaves or the token is compromised, pipelines break. Deploy tokens are scoped to a project or group and are the recommended approach for CI/CD.

Troubleshooting

403 Forbidden when pulling

The token doesn’t have the read_registry scope, or the user/token doesn’t have access to the project. Check the deploy token scopes and verify the project visibility settings.

DOCKER_AUTH_CONFIG not working

Make sure the variable type is set to Variable, not File. Also check that the JSON is valid — a missing comma or bracket will silently fail. Test the JSON locally with echo '$DOCKER_AUTH_CONFIG' | python3 -m json.tool.

Image not found

Double-check the full image path. The format is registry.gitlab.com/group/project/image:tag. Use $CI_REGISTRY_IMAGE in same-project pipelines to avoid typos.

Conclusion

For same-project images, GitLab handles authentication automatically — just use $CI_REGISTRY_IMAGE. For cross-project access, create a deploy token and configure DOCKER_AUTH_CONFIG. If you’re working with Docker images that have build issues in CI/CD, check out How to Remove All None Tag Docker Images to clean up dangling images from failed builds. For WSL Docker permission issues, see Resolving WSL Permission Denied When Connecting to Docker Daemon.