Proxy Docker images via GitLab

Brett Weir, December 28, 2022

Docker Hub rate limits container image pulls. These limits are easy to hit in a CI environment. This is especially likely if your CI environment is behind a NAT firewall. Thus, it is very important to enable a container proxy to ensure continuity of your CI service.

GitLab's Dependency Proxy feature allows you to proxy Docker Hub containers through GitLab.

CONTAINER_PROXY variable

It is highly recommended to NOT use GitLab's built-in dependency proxy variables directly. These are:

This is for the following reasons:

The BrettOps container pipeline takes an alternative approach, and defines a layer of indirection for all of the above, as follows:

# registry
CONTAINER_PROXY: "${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/"

# registry with port number
CONTAINER_PROXY_SERVER: "${CI_DEPENDENCY_PROXY_SERVER}"

# registry password
CONTAINER_PROXY_PASSWORD: "${CI_DEPENDENCY_PROXY_PASSWORD}"

# registry username
CONTAINER_PROXY_USER: "${CI_DEPENDENCY_PROXY_USER}"

Add a forward slash

You would think, and most documentation would suggest, that an appropriate way to use a container proxy is like so:

${MY_PROXY}/my-container:latest

Not so fast! What happens if ${MY_PROXY} is not defined? This happens more often than you might think. You'll spend a good chunk of time setting up your CI pipeline to work perfectly with all the proxy variables tightly configured. In the meantime, you'll forget that once upon a time, you needed to build this image locally, and the next time you try doing this, you'll stumble over setting up the environment variables properly for a local build.

Nobody needs that. By adding a single forward slash to the variable value, you make it optional! At this point, building locally is no longer a problem.

This is why the container pipeline defines the CONTAINER_PROXY variable to equal the following default:

${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/

Log in to the registry

Before you can use the proxy registry to build new containers, you must authenticate. Don't spin cycles trying to debug a failing pipeline because you forgot to log in!

The following one-liner is the most succinct method I've found for Docker login in a CI pipeline.

echo "$CONTAINER_PROXY_PASSWORD" | docker login "$CONTAINER_PROXY_SERVER" -u "$CONTAINER_PROXY_USER" --password-stdin

This is much easier than trying to inject a complete config.json into a CI environment, as these commands can stack if you discover you need access to more registries:

echo "$CONTAINER_PROXY_PASSWORD" | docker login "$CONTAINER_PROXY_SERVER" -u "$CONTAINER_PROXY_USER" --password-stdin
echo "$CONTAINER_PROXY_2_PASSWORD" | docker login "$CONTAINER_PROXY_2_SERVER" -u "$CONTAINER_PROXY_2_USER" --password-stdin
echo "$CONTAINER_PROXY_NEW_PASSWORD" | docker login "$CONTAINER_PROXY_NEW_SERVER" -u "$CONTAINER_PROXY_NEW_USER" --password-stdin
# ...

Create a proxy group

GitLab declares the following two proxy registry variables:

Both of these are not ideal. If your organization has any kind of nesting in its GitLab group structure, you probably also have users that have direct access to some groups and not others, or users who have access to projects, but not to the groups that contain those projects.

GitLab users must have Guest privilege directly attached to a group to use the proxy registry of that group. This is an easy one to get bit by, so watch out.

Use the pipeline

The container pipeline is already configured to work the way that you expect. CONTAINER_PROXY is always defined when the pipeline is included, though it is still recommended to declare it globally in your GitLab group.