GitLab + Helm
Learn how to package, publish, and deploy Helm charts with GitLab. From chart structure to OCI registry publishing and environment-specific deployments, build a complete Helm workflow in CI/CD.
Helm is the de facto package manager for Kubernetes. It turns a collection of Kubernetes manifests into a reusable, versioned, configurable chart. Instead of maintaining separate YAML files for staging and production, you maintain one chart with environment-specific values files. Instead of copying manifests between projects, you package the chart and publish it to a registry. Helm brings structure and reusability to Kubernetes deployments, and GitLab provides the CI/CD platform to build, publish, and deploy those charts.
GitLab and Helm fit together naturally. GitLab Container Registry supports OCI artifacts, which means you can store Helm charts in the same registry as your Docker images. GitLab CI/CD can package the chart, push it to the registry, and deploy it to Kubernetes using the GitLab agent. The whole workflow — from chart change to production deployment — lives in one platform, with one set of credentials, one audit trail, and one place to look when something goes wrong.
The alternative — a separate chart repository like ChartMuseum or Artifactory — adds another service to maintain, another set of credentials to manage, and another place where things can break. For teams already using GitLab, the OCI registry is the path of least resistance. It's not the only option, but it's the one that requires the least additional infrastructure.
Chart Structure
Values Files
helm package
OCI Registry
helm push, pull with helm pull.helm upgrade --install
helm rollback
A Helm chart is a directory with a specific structure. At minimum, it contains a Chart.yaml (metadata: name, version, description), a values.yaml (default configuration), and a templates/ directory (the Kubernetes manifests with template placeholders). Helm renders the templates with the values to produce the final Kubernetes YAML. This separation — templates for structure, values for configuration — is what makes charts reusable across environments.
The Chart.yaml is the chart's identity. The name and version fields are required. The version field follows semantic versioning and must be bumped every time the chart changes — this is how Helm tracks releases. The appVersion field is separate and refers to the application version (e.g., the Docker image tag), which is often set dynamically during CI/CD.
The templates/ directory contains standard Kubernetes manifests with Go template syntax. Helpers like {{ .Values.image.tag }} and {{ .Release.Name }} inject values from values.yaml and Helm's built-in release metadata. The _helpers.tpl file is a convention for reusable template snippets — name generation, labels, annotations — that many templates share. Understanding these conventions is essential for writing maintainable charts.
- Use semantic versioning for
version; bump it on every chart change - Keep
appVersionseparate fromversion; set it dynamically in CI/CD - Use
_helpers.tplfor name generation and labels - Provide sensible defaults in
values.yaml; override per environment - Use
.helmignoreto exclude test files, docs, and CI configs from packaging
Packaging a chart is a single command: helm package my-app produces my-app-0.1.0.tgz, a versioned archive of the chart. This is the artifact that gets published and consumed. In a GitLab pipeline, packaging is typically a build-stage job that runs after chart validation and linting. The output is an artifact that later jobs can use.
Publishing to the GitLab Container Registry uses Helm's OCI support. You authenticate with helm registry login using the standard CI registry credentials ($CI_REGISTRY_USER, $CI_REGISTRY_PASSWORD), then push the packaged chart with helm push my-app-0.1.0.tgz oci://$CI_REGISTRY_IMAGE/charts. The chart is stored alongside your Docker images, tagged by version, and can be pulled from any cluster that has registry access.
The chart version and the app version should be set dynamically during CI/CD. The chart version is typically set to the git commit SHA or a semantic version derived from git tags. The appVersion is set to the image tag. This ensures that every chart release is traceable to a specific commit and a specific image, which is essential for rollbacks and audits. A chart published without version information is useless for reproducibility.
- Version is required: Helm refuses to push a chart without a version
- Version must be unique: Pushing the same version twice overwrites it (use commit SHA to avoid this)
- Registry credentials: Use
$CI_REGISTRY_USERand$CI_REGISTRY_PASSWORD— the CI job token also works - Path structure: The OCI path is arbitrary; use a consistent convention like
charts/