GitLab CI can deploy Helm charts to Kubernetes by leveraging its CI/CD features to automate the process of building, packaging, and releasing your application.

Let’s see it in action. Imagine you have a simple web application, and you want to deploy it to a Kubernetes cluster using Helm. Your GitLab repository contains your application code, a Dockerfile to build your container image, and a Helm chart in a charts/ directory.

Here’s a snippet of a .gitlab-ci.yml file that accomplishes this:

stages:
  - build
  - deploy

variables:
  IMAGE_NAME: registry.gitlab.com/$CI_PROJECT_PATH/$CI_COMMIT_REF_SLUG
  HELM_CHART_PATH: charts/my-app

build_image:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
    - docker push $IMAGE_NAME:$CI_COMMIT_SHA
    - docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
    - docker push $IMAGE_NAME:latest
  only:
    - main

deploy_to_kubernetes:
  stage: deploy
  image: alpine/helm:latest
  script:
    - helm upgrade --install my-release $HELM_CHART_PATH \
        --namespace my-namespace \
        --set image.repository=$IMAGE_NAME \
        --set image.tag=$CI_COMMIT_SHA \
        --create-namespace
  environment:
    name: production
    url: http://my-app.example.com
  only:
    - main

In this pipeline:

  1. build_image job:

    • Uses the docker:latest image and docker:dind (Docker-in-Docker) service to build a Docker image for your application.
    • Logs into the GitLab Container Registry using predefined CI/CD variables ($CI_REGISTRY_USER, $CI_REGISTRY_PASSWORD, $CI_REGISTRY).
    • Tags the image with the commit SHA ($CI_COMMIT_SHA) for immutability and latest.
    • Pushes both tags to the GitLab Container Registry.
  2. deploy_to_kubernetes job:

    • Uses the alpine/helm:latest image, which conveniently includes the Helm client.
    • Executes helm upgrade --install. This command is idempotent: if my-release doesn’t exist in the my-namespace Kubernetes namespace, it will be installed; otherwise, it will be upgraded.
    • $HELM_CHART_PATH points to your Helm chart directory.
    • --set image.repository=$IMAGE_NAME and --set image.tag=$CI_COMMIT_SHA override values within your Helm chart’s values.yaml file, dynamically injecting the newly built container image. This is crucial for deploying the correct version of your application.
    • --create-namespace ensures the specified namespace exists before deployment.
    • The environment block links this deployment to a GitLab environment, providing visibility and tracking.

This setup ensures that every time you push to the main branch, a new container image is built and pushed, and then Helm is used to deploy that specific image version to your Kubernetes cluster.

The real power here is how GitLab CI acts as the orchestrator. It handles the authentication to your container registry, manages the build environment, and then provides the Helm client to interact with your Kubernetes cluster. You’ll need to configure your Kubernetes credentials as a CI/CD variable (e.g., KUBE_CONFIG) or use a service account with appropriate permissions, and ensure your GitLab Runner has access to the Kubernetes cluster.

The helm upgrade --install command is a bit magical because it combines installation and upgrade logic. If you’re managing multiple Helm releases or complex dependencies, you might find yourself needing to manage Helm repositories within your CI pipeline using helm repo add and helm repo update before running helm upgrade.

Understanding how Helm charts are structured, particularly the values.yaml file and how to override its values using --set or --set-string, is key to customizing deployments. You can also pass entire YAML files using --values your-custom-values.yaml.

The next logical step is to explore how to manage different environments (staging, production) with separate Helm releases and configurations, potentially using different branches or tags to trigger deployments to those environments.

Want structured learning?

Take the full Gitlab-ci course →