Kubernetes
GitLab
Container Registry
DevOps
CI/CD

Connect kubernetes to GitLab Container Registry

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

To pull container images from GitLab's Container Registry in Kubernetes, you need to create a Kubernetes Secret containing your GitLab credentials and reference it as an imagePullSecrets in your pod or deployment spec. The secret stores the registry URL (registry.gitlab.com), username, and either a personal access token or a deploy token as the password. Without this secret, Kubernetes cannot authenticate with the private registry and pod creation fails with ErrImagePull or ImagePullBackOff.

Step 1: Create a GitLab Access Token

bash
1# Option A: Personal Access Token (PAT)
2# Go to GitLab > User Settings > Access Tokens
3# Create a token with 'read_registry' scope
4# Username: your GitLab username
5# Password: the generated token
6
7# Option B: Deploy Token (recommended for CI/CD)
8# Go to Project > Settings > Repository > Deploy Tokens
9# Create a token with 'read_registry' scope
10# Username: the auto-generated deploy token username
11# Password: the deploy token value
12
13# Option C: Group Deploy Token
14# Go to Group > Settings > Repository > Deploy Tokens
15# Works for all projects in the group

Deploy tokens are preferred over personal access tokens because they are scoped to a project or group and can be revoked without affecting the user's account.

Step 2: Create Kubernetes Secret

bash
1# Create the docker-registry secret
2kubectl create secret docker-registry gitlab-registry \
3    --docker-server=registry.gitlab.com \
4    --docker-username=deploy-token-username \
5    --docker-password=your-deploy-token \
6    --docker-email=[email protected] \
7    --namespace=default
8
9# Verify the secret was created
10kubectl get secret gitlab-registry -o yaml
bash
1# For self-hosted GitLab, use your registry URL
2kubectl create secret docker-registry gitlab-registry \
3    --docker-server=registry.your-gitlab.com \
4    --docker-username=deploy-token-username \
5    --docker-password=your-deploy-token \
6    --docker-email=[email protected]

The secret type docker-registry stores credentials in the format Docker expects for registry authentication.

Step 3: Reference in Pod/Deployment

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: my-app
5  namespace: default
6spec:
7  replicas: 2
8  selector:
9    matchLabels:
10      app: my-app
11  template:
12    metadata:
13      labels:
14        app: my-app
15    spec:
16      containers:
17        - name: my-app
18          image: registry.gitlab.com/mygroup/myproject:latest
19          ports:
20            - containerPort: 8080
21      imagePullSecrets:
22        - name: gitlab-registry

The imagePullSecrets field tells Kubernetes which secret to use when pulling the image. Without it, the kubelet attempts anonymous access and fails for private registries.

Creating the Secret from YAML

yaml
1# gitlab-registry-secret.yaml
2apiVersion: v1
3kind: Secret
4metadata:
5  name: gitlab-registry
6  namespace: default
7type: kubernetes.io/dockerconfigjson
8data:
9  .dockerconfigjson: <base64-encoded-docker-config>
bash
1# Generate the base64 value
2echo -n '{"auths":{"registry.gitlab.com":{"username":"deploy-token","password":"your-token","auth":"base64(user:pass)"}}}' | base64
3
4# Or create from an existing Docker config
5kubectl create secret generic gitlab-registry \
6    --from-file=.dockerconfigjson=$HOME/.docker/config.json \
7    --type=kubernetes.io/dockerconfigjson

Setting Default imagePullSecrets per Namespace

bash
1# Patch the default service account to always use the registry secret
2kubectl patch serviceaccount default \
3    -n my-namespace \
4    -p '{"imagePullSecrets": [{"name": "gitlab-registry"}]}'
5
6# Now pods in this namespace automatically use the secret
7# No need for imagePullSecrets in every deployment spec
yaml
1# Or via YAML
2apiVersion: v1
3kind: ServiceAccount
4metadata:
5  name: default
6  namespace: my-namespace
7imagePullSecrets:
8  - name: gitlab-registry

Patching the default service account eliminates the need to add imagePullSecrets to every pod spec in the namespace.

GitLab CI/CD Integration

yaml
1# .gitlab-ci.yml — build and push image, then deploy to K8s
2stages:
3  - build
4  - deploy
5
6build:
7  stage: build
8  image: docker:latest
9  services:
10    - docker:dind
11  script:
12    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
13    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
14    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
15
16deploy:
17  stage: deploy
18  image: bitnami/kubectl:latest
19  script:
20    - kubectl set image deployment/my-app
21        my-app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
22        --namespace=production

GitLab CI provides built-in variables ($CI_REGISTRY, $CI_REGISTRY_USER, $CI_REGISTRY_PASSWORD) for registry authentication. The Kubernetes secret must already exist in the cluster for the deployed pods to pull the image.

Troubleshooting

bash
1# Check if the pod can pull the image
2kubectl describe pod my-app-pod
3# Look for events:
4# Failed to pull image "registry.gitlab.com/...": unauthorized
5# Error: ErrImagePull
6# Error: ImagePullBackOff
7
8# Verify the secret exists and is correct
9kubectl get secret gitlab-registry -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d
10
11# Test registry access manually
12docker login registry.gitlab.com -u deploy-token -p your-token
13docker pull registry.gitlab.com/mygroup/myproject:latest
14
15# Check if the secret is referenced in the pod spec
16kubectl get pod my-app-pod -o jsonpath='{.spec.imagePullSecrets}'
17
18# Check token permissions
19# The token must have 'read_registry' scope

Multiple Registries

yaml
1# Create secrets for each registry
2# kubectl create secret docker-registry gitlab-reg ...
3# kubectl create secret docker-registry dockerhub-reg ...
4
5apiVersion: apps/v1
6kind: Deployment
7spec:
8  template:
9    spec:
10      containers:
11        - name: app
12          image: registry.gitlab.com/group/app:latest
13        - name: sidecar
14          image: docker.io/library/nginx:latest
15      imagePullSecrets:
16        - name: gitlab-reg
17        - name: dockerhub-reg

Multiple imagePullSecrets can be specified. Kubernetes tries each secret against the corresponding registry URL.

Common Pitfalls

  • Token without read_registry scope: Deploy tokens or personal access tokens must have the read_registry scope. A token with only api or read_repository scope cannot pull images. Check the token permissions in GitLab's UI.
  • Secret in wrong namespace: Kubernetes secrets are namespace-scoped. If the deployment is in namespace production but the secret is in default, the pod cannot find the secret. Create the secret in the same namespace as the deployment.
  • Expired deploy token: Deploy tokens can expire. When they do, pods start failing with ImagePullBackOff. Set up monitoring for token expiration and rotate tokens before they expire. Consider using tokens without expiration for long-running clusters.
  • Forgetting imagePullSecrets in the pod spec: Creating the secret is not enough — the pod spec must reference it via imagePullSecrets. Alternatively, patch the default service account to include the secret automatically.
  • Using the wrong registry URL for self-hosted GitLab: The default registry URL is registry.gitlab.com for GitLab.com. Self-hosted GitLab instances use a custom URL (often registry.your-domain.com). The --docker-server value must match the registry URL in the image reference.

Summary

  • Create a docker-registry secret with kubectl create secret docker-registry using GitLab credentials
  • Reference the secret in imagePullSecrets in your deployment spec
  • Use deploy tokens with read_registry scope (preferred over personal access tokens)
  • Patch the default service account to avoid adding imagePullSecrets to every pod spec
  • The secret must be in the same namespace as the deployment
  • Use kubectl describe pod to diagnose ErrImagePull and ImagePullBackOff errors

Course illustration
Course illustration

All Rights Reserved.