Helm in CI/CD
In CI/CD, Helm should lint, render, validate against the API server, deploy with immutable image tags, wait for readiness, and leave an auditable release history. Skip direct helm upgrade when ArgoCD owns the release.
Direct Helm vs GitOps
| Approach | Use when | Avoid when |
|---|---|---|
Direct helm upgrade in CI | Client has no GitOps; CI is the deploy authority | ArgoCD/Flux manages the same release |
| GitOps (update values in Git) | ArgoCD or Flux is the deploy authority | You need emergency hotfix without a PR process |
| Helm in CI, promote via OCI | Chart registry pattern; cluster pulls versioned charts | Simple app with no chart packaging step |
Chart Versioning
| Field | What to bump | Example |
|---|---|---|
Chart.yaml version | Any template or values default change | 1.4.0 → 1.4.1 |
Chart.yaml appVersion | Default app version label | "2.1.0" |
image.tag in values | Every deploy — use commit SHA or digest | abc123f |
Pipeline Script
This script is a direct Helm deployment pipeline: validate the chart, then upgrade the release with the new image tag. Use it only when CI is the approved deploy mechanism for the app.
set -euo pipefail
RELEASE=web-api
NAMESPACE=app-prod
CHART=charts/web-api
IMAGE_TAG="${IMAGE_TAG:?CI must provide IMAGE_TAG}"
helm dependency build "$CHART"
helm lint "$CHART"
helm template "$RELEASE" "$CHART" \
--namespace "$NAMESPACE" \
--values "$CHART/values-prod.yaml" \
--set image.tag="$IMAGE_TAG" \
> rendered.yaml
kubectl apply --dry-run=server -f rendered.yaml
helm upgrade --install "$RELEASE" "$CHART" \
--namespace "$NAMESPACE" \
--create-namespace \
--values "$CHART/values-prod.yaml" \
--set image.tag="$IMAGE_TAG" \
--wait \
--atomic \
--timeout 10m
kubectl rollout status deploy/"$RELEASE" -n "$NAMESPACE" --timeout=10m
helm history "$RELEASE" -n "$NAMESPACE"
GitHub Actions
This workflow wraps the Helm pipeline in GitHub Actions with a production environment gate. Use it when deploys require approval and the runner has controlled access to the target cluster.
name: helm-deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: azure/setup-kubectl@v4
- uses: azure/setup-helm@v4
with:
version: v3.14.0
- name: Configure kubeconfig
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
- name: Lint and deploy
env:
IMAGE_TAG: ${{ github.sha }}
run: |
helm dependency build charts/web-api
helm lint charts/web-api
helm template web-api charts/web-api -n app-prod \
-f charts/web-api/values-prod.yaml \
--set image.tag="$IMAGE_TAG" > rendered.yaml
kubectl apply --dry-run=server -f rendered.yaml
helm upgrade --install web-api charts/web-api \
-n app-prod --create-namespace \
-f charts/web-api/values-prod.yaml \
--set image.tag="$IMAGE_TAG" \
--wait --atomic --timeout 10m
--wait vs --atomic
| Flag | Behavior |
|---|---|
--wait | Blocks until Deployments/StatefulSets/Jobs reach ready state |
--atomic | Implies --wait; auto-rolls back to previous revision on failure |
--timeout 10m | Max wait time — tune per app startup duration |
OCI Chart Registry
Use OCI chart registries when the client wants versioned Helm charts stored alongside container artifacts. Package and push the chart, then install or upgrade by immutable chart version.
helm registry login registry.example.com
helm package charts/web-api --version 1.2.3 --app-version "${IMAGE_TAG}"
helm push web-api-1.2.3.tgz oci://registry.example.com/charts
# Install from OCI registry.
helm upgrade --install web-api oci://registry.example.com/charts/web-api \
--version 1.2.3 \
--namespace app-prod \
--values values-prod.yaml
Rollback
Use these commands when a direct Helm deployment needs to return to a previous revision. Check release history first so you roll back to the intended known-good version.
helm status web-api -n app-prod
helm history web-api -n app-prod
helm rollback web-api 12 -n app-prod --wait --timeout 10m
kubectl rollout status deploy/web-api -n app-prod
When ArgoCD Owns Helm
- Do not run
helm upgradeagainst an ArgoCD-managed release unless the incident process allows emergency drift. - Update values in Git, let ArgoCD sync, then troubleshoot the Application if sync fails.
- Use
argocd app diffand savedhelm templateoutput to compare desired state.
SRE Rules
- Deploy image digests or commit-SHA tags — never
latestin production. - Render manifests in CI before applying — catch template errors without touching the cluster.
- Separate build credentials from deploy credentials.
- Pin Helm CLI version in CI to match what operators use locally.