<!--
  canonical-url: https://www.avonture.be/blog/gitlab-using-private-images
  published:     2025-06-06
  This is a static plain-Markdown mirror generated at build time.
  Visit the canonical URL above for the fully rendered page, with images and interactive components.
-->

# GitLab - Using Docker private images

> Use a private Docker image from Docker Hub in your GitLab CI/CD pipeline. Learn to create a read-only token and set up the necessary GitLab CI/CD variables for authentication.

---
![GitLab - Using Docker private images](/img/v2/gitlab.webp)

**TL;DR**

To use a private Docker image from Docker Hub in a GitLab CI/CD pipeline, first generate a read-only personal access token from your Docker Hub settings. Then, in your GitLab project's CI/CD settings, create two protected and masked variables: `DOCKER_HUB_USERNAME` for your Docker username and `DOCKER_HUB_TOKEN_RO` for the access token. Finally, in your `.gitlab-ci.yml`, use `docker login` with these variables to authenticate before pulling the image.

In one of my GitLab CI, I came across the need to use a Docker image stored on `hub.docker.com` in a private repository.

I couldn't simply do a `docker pull my_image` because, first of all, I had to authenticate myself in the CI before I could retrieve the image (it's a private one).

This article is a how-to and explains how to do it.

*Two sibling articles about the same CI: [GitLab - Using an SSH key to connect to private repo](/blog/gitlab-runner-ssh-key) for private **git** repositories, and [GitLab - Running Docker-out-of-Docker in your CI](/blog/gitlab-docker-out-of-docker) if your job needs to run `docker` commands at all.*

Here's the payoff once the token and variables are in place: the job authenticates and pulls the private image instead of failing with `denied: requested access to the resource is denied`.

## Why It Works

* The Docker Hub token is scoped `Read-Only`, so even if it leaked, it could only pull images — never push or modify anything on your account.
* The username and token live as hidden, protected CI/CD variables, never in `.gitlab-ci.yml` itself — GitLab won't even echo them if you turn on full debug tracing.

## Create a token

You'll need to create a token first. Please visit this page: <https://app.docker.com/settings/personal-access-tokens>. You'll need to sign in, of course.

Click on the `Generate new token` button and create a new one. Make sure to correctly specify the scope. To be able to access private images, you should select `Read-Only` and not `Public Repo Read-Only` (because the last one didn't give access to private images).

Once created, Docker will provide you a small help screen where you'll read that you can connect to Docker by using the token just as if it were your password.

## Create two CI/CD variables in your repository page

In your GitLab CI/CD settings page, add two new variables:

* The first one will be called `DOCKER_HUB_USERNAME`. The value to enter here is your Docker account name (like `christophe` or `my_company` if the image is stored in a business account) and
* the second one will be called `DOCKER_HUB_TOKEN_RO`. The value to enter here is the token you've received.

Make sure variables are hidden and protected. This will prevent GitLab from echoing the value in any output like when you initialize `CI_DEBUG_TRACE=true` ([doc](https://docs.gitlab.com/ci/variables/variables_troubleshooting/#enable-debug-logging)) for full debug.

![The two variables have been created](./images/variables.webp)

> **Note:**
>
> You can add the variables in your repository's CI/CD settings page or at a higher level like the group level or at the instance level (you should be an admin for this).

## Your gitlab-ci.yml file

Once done, here is how to connect to Docker in your `.gitlab-ci.yml` file:

**.gitlab-ci.yml**

```yaml
demo:
  image: docker:latest
  before_script:
    - | # Do a secure login
      echo "$DOCKER_HUB_TOKEN_RO" | docker login -u "$DOCKER_HUB_USERNAME" --password-stdin
  script:
    - | # Download the image
      docker pull your_private_image
    # - | # Use it
    #   docker run [...] your_private_image [...]
```

## Conclusion

A read-only token and two protected, masked CI/CD variables are all it takes to pull a private Docker Hub image in your pipeline, without ever committing a credential to your repo. See [GitLab - Using an SSH key to connect to private repo](/blog/gitlab-runner-ssh-key) for the same problem applied to a private **git** repository instead of a private image.
