Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for GitLab CI/CD By James Joyner IV · · 9 min read

GitLab CI + Helm: Repeatable Kubernetes Deploys Without the Auto DevOps Magic

Deploy to Kubernetes from GitLab CI with Helm — linting, templating, gated upgrades and rollbacks — keeping the control Auto DevOps hides from you.

  • #gitlab
  • #cicd
  • #helm
  • #kubernetes
  • #deployment
  • #auto-devops

GitLab’s Auto DevOps is genuinely impressive: point it at a repo and it’ll build, test, scan, and deploy to Kubernetes with zero pipeline code. I’ve also spent more hours than I’d like debugging why an Auto DevOps deploy did something I didn’t expect, because the logic is hidden inside templates I didn’t write. For a lot of teams, the better trade is an explicit Helm-based pipeline: a bit more YAML up front, total visibility into what ships.

This is how to build a Helm deployment pipeline you actually understand.

Why Helm, and why explicit

Helm packages your Kubernetes manifests into a versioned chart with templated values, so the same chart deploys to staging and production with different value files. Two properties make it pipeline-friendly:

  • Atomic upgrades with rollback. helm upgrade --atomic rolls back automatically if the release fails to become healthy. No half-deployed limbo.
  • Release history. Helm tracks every revision, so helm rollback to the last good state is one command — invaluable at 2am.

Auto DevOps actually uses Helm under the hood. Going explicit just means you own the chart and the values, so there’s no magic to reverse-engineer when something’s off.

Lint and template before you ever deploy

The cheapest bugs to fix are the ones caught before they touch a cluster. Two jobs do that:

stages: [validate, deploy]

helm_lint:
  stage: validate
  image: alpine/helm:3.15
  script:
    - helm lint ./chart --values ./chart/values-staging.yaml

helm_template:
  stage: validate
  image: alpine/helm:3.15
  script:
    - helm template myapp ./chart --values ./chart/values-staging.yaml > rendered.yaml
    - cat rendered.yaml
  artifacts:
    paths: [rendered.yaml]

helm lint catches malformed charts. helm template renders the actual manifests that would be applied — and saving them as an artifact means a reviewer can diff the rendered Kubernetes output in the merge request. Seeing the real, rendered YAML before it ships catches the “oops, that env var interpolated to empty string” class of bug every time.

Deploy to staging automatically, production on a click

The deploy jobs differ only by values file and gating. Staging deploys on merge to default; production waits for a human:

deploy_staging:
  stage: deploy
  image: alpine/helm:3.15
  script:
    - helm upgrade --install myapp ./chart
        --namespace staging
        --values ./chart/values-staging.yaml
        --set image.tag=$CI_COMMIT_SHA
        --atomic --timeout 5m
  environment:
    name: staging
    url: https://staging.example.com
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

deploy_production:
  stage: deploy
  image: alpine/helm:3.15
  script:
    - helm upgrade --install myapp ./chart
        --namespace production
        --values ./chart/values-production.yaml
        --set image.tag=$CI_COMMIT_SHA
        --atomic --timeout 10m
  environment:
    name: production
    url: https://example.com
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: manual

A few details that matter in practice:

  • --install makes upgrade idempotent — it installs on first run, upgrades after. One job handles both.
  • --set image.tag=$CI_COMMIT_SHA pins the deploy to the exact commit, so the image and the chart are always in lockstep. Never deploy :latest.
  • --atomic is the safety net: if the new release doesn’t become healthy within --timeout, Helm rolls back automatically. You wake up to “deploy failed and self-reverted” instead of “production is half-broken.”
  • when: manual on production keeps a human in the loop.

Make rollback a first-class button

When --atomic doesn’t catch it — a deploy that’s technically healthy but functionally broken — you want a one-click rollback, not a scramble through kubectl:

rollback_production:
  stage: deploy
  image: alpine/helm:3.15
  script:
    - helm rollback myapp --namespace production
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: manual
  environment:
    name: production
    action: prepare

A manual rollback job sitting in the pipeline turns “oh no, how do I revert” into “click the button.” During an incident, that pre-built escape hatch is worth a lot.

Connecting to the cluster

Authenticate to your cluster via the GitLab Kubernetes agent (the modern, pull-based approach) rather than storing a kubeconfig as a CI variable. The agent gives the pipeline a scoped, short-lived connection — same philosophy as OIDC for cloud creds. Our GitLab CI/CD guides cover the agent and the broader Kubernetes deploy story in depth.

Where AI fits

Helm’s failure modes are legendarily cryptic — a templating error or a values-merge surprise produces a wall of Go-template noise. This is prime AI territory: paste the helm template output or the error and ask “why did this value come out empty?” or “what’s the difference between my staging and production rendered manifests?” The model reads templated YAML faster than you can.

For the chart and pipeline changes themselves, our AI Code Review assistant flags risky deploy diffs — a removed resource limit, a changed replica count, a values key that no longer matches the template. The same principle as everywhere in this stack: AI explains and flags, the human clicks deploy.

The trade you’re making

Auto DevOps is great when you want zero config and accept the magic. An explicit Helm pipeline costs you some YAML and buys you complete visibility: you can read every line of what ships, diff the rendered manifests in review, and roll back with one click. For anything past a toy project, I’ll take the visibility every time.

Rendered manifests, deploy advice, and AI-generated explanations are assistive. Verify Helm output and deployment behavior against your own cluster before relying on it in production.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.