TL;DR

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

ApproachUse whenAvoid when
Direct helm upgrade in CIClient has no GitOps; CI is the deploy authorityArgoCD/Flux manages the same release
GitOps (update values in Git)ArgoCD or Flux is the deploy authorityYou need emergency hotfix without a PR process
Helm in CI, promote via OCIChart registry pattern; cluster pulls versioned chartsSimple app with no chart packaging step

Chart Versioning

FieldWhat to bumpExample
Chart.yaml versionAny template or values default change1.4.0 → 1.4.1
Chart.yaml appVersionDefault app version label"2.1.0"
image.tag in valuesEvery deploy — use commit SHA or digestabc123f

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.

bash helm-pipeline.sh
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.

yaml .github/workflows/helm-deploy.yaml
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

FlagBehavior
--waitBlocks until Deployments/StatefulSets/Jobs reach ready state
--atomicImplies --wait; auto-rolls back to previous revision on failure
--timeout 10mMax 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.

bash oci-chart.sh
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.

bash helm-rollback.sh
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 upgrade against 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 diff and saved helm template output to compare desired state.

SRE Rules

  • !Deploy image digests or commit-SHA tags — never latest in 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.