In order to pull image to your cluster from a private gitlab registry, you will need to specify to Kubernetes the image pull secrets to use.

The best way I have found to do this is with a access token that only has access to read the registry on Gitlab, and specifying that as the password to the Kubernetes secret.

Here's a guide to get it working:

Get an access token

First thing you will need is an access token from Gitlab which is authenticated in order to read the registry.

You should not give this token any more access than that to lower the attack vector if exposed.

With the advent of Gitlab Deploy tokens, you can now also limit the group to which a deploy token is scoped.

Navigate to your group settings, then CI / CD . There is a section called Deploy Tokens .

Create a new token, with only read_registry box ticked.

I find it best to give the Deploy Token a username to keep it consistent.

After creation a little dialog box with pop up.

Dialog box containing username and password of the created deploy token
This token is no longer valid :) 

Copy the password, as you won't see it again :)

Create a docker credentials file

In order for Kubernetes to use the credentials, we need to first give it the credentials, and then assign those credentials to either the service account that will be used to pull the images, or specify them directly on the deployment files that need to pull these images.

So first let's create the secret.

The format of the secret is in the format of a .dockerconfigjson file.

The general format of this file is

{
    "auths": {
        "https://registry.gitlab.com":{
            "username":"REGISTRY_USERNAME",
            "password":"REGISTRY_PASSWORD",
            "email":"REGISTRY_EMAIL",
            "auth":"BASE_64_BASIC_AUTH_CREDENTIALS (see below)"
    	}
    }
}

The base 64 basic credentials mentioned above are the username and password in basic credentials format {username}:{password} , encoded with base64 format.

To achieve this simply run :

echo -n "{REGISTRY_USERNAME}:{REGISTRY_PASSWORD}" | base64

Create a file with above mentioned json format, and then base64 encode it for the Kubernetes secret.

cat .dockerconfigjson | base64

This will output the base64 you need for the registry secret.

Create a Kubernetes secret

Next we need to create the Kubernetes secret

Create a file called registry-credentials.yml and add the following content

apiVersion: v1
kind: Secret
metadata:
  name: registry-credentials
  namespace: default
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: BASE_64_ENCODED_DOCKER_FILE

Replace BASE_64_ENCODED_DOCKER_FILE with the content with the base64 output you received above.

Now we can create the secret in our cluster.

kubectl apply -f registry-credentials.yml

Adding the ImagePullSecrets

There are two main ways to tell Kubernetes to use the credentials to pull images.

One way is by assigning the secret to the service account which will be pulling the  images, and the other is to specify them directly on the deployment which is using the private images.

Specifying on a deployment

This is a fairly easy approach, but does cause a bit more management in Deployments

On your deployment file where you are referencing the private image, simply add the imagePullSecrets

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
spec:
  template:
    metadata:
      ...
    spec:
      containers:
      - name: ...
      imagePullSecrets: 
      - name: registry-credentials

That deployment should now use those credentials to pull images

Specify at a service account level.

Say we want a service account to have access to our registry and always use the secrets when pulling images, we can specify it on our service account directly.

We can either directly patch the service account (Not recommended, see second approach)

kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "registry-credentials"}]}'

I don't recommend this approach as after the command is run there is no evidence anywhere to see what is using the credentials.

My preferred approach is to always use yaml files, which can be tracked in version control. This makes it much easier to see what will break when changing something.

In a yaml file called default.service-account.yml , specify the default service account with imagePullSecrets

apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
imagePullSecrets:
- name: registry-credentials

and then  apply that.

kubectl apply -f default.service-account.yml

Now you should be able to pull images from you private registry

TLDR;

The short version of this for really fast testing:

Create the deploy token as mentioned above

Create the secret

kubectl create secret docker-registry registry-credentials --docker-server=https://registry.gitlab.com --docker-username=REGISTRY_USERNAME --docker-password=REGISTRY_PASSWORD --docker-email=REGISTRY_EMAIL

Attach secret to default service account

kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "registry-credentials"}]}'

It is better to keep the credentials in Yaml files though, to make them shareable across namespaces.

Happy Hacking !