Jenkins, a ubiquitous automation server, often finds itself at the nexus of modern software delivery, and one of its most common integration points is with Helm, the package manager for Kubernetes. When you’re tasked with deploying Helm charts from Jenkins, you’re essentially bridging the gap between your CI/CD system and your Kubernetes cluster.

The most surprising truth about deploying Helm charts from Jenkins is that the complexity often lies not in Helm itself, but in managing the Jenkins environment to safely and securely execute Helm commands. This means dealing with Kubernetes credentials, Helm’s state, and the ephemeral nature of CI jobs.

Let’s visualize this. Imagine a Jenkins pipeline script (Jenkinsfile) that orchestrates the deployment:

pipeline {
    agent any

    environment {
        KUBECONFIG_FILE = credentials('my-kubeconfig-secret') // Jenkins secret for kubeconfig
        HELM_RELEASE_NAME = 'my-app-release'
        HELM_CHART_PATH = 'charts/my-app' // Path to your chart within the repo
        NAMESPACE = 'staging'
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Install/Upgrade Helm Chart') {
            steps {
                script {
                    // Ensure kubeconfig is available for helm to use
                    sh "mkdir -p \$HOME/.kube"
                    sh "echo \"\$KUBECONFIG_FILE\" > \$HOME/.kube/config"
                    sh "chmod 600 \$HOME/.kube/config"

                    // Install or upgrade the Helm chart
                    // The --install flag ensures it creates if it doesn't exist
                    // The --atomic flag makes the upgrade rollback on failure
                    // The --wait flag ensures the pipeline waits for resources to be ready
                    sh """
                        helm upgrade --install ${HELM_RELEASE_NAME} ${HELM_CHART_PATH} \
                            --namespace ${NAMESPACE} \
                            --values ${HELM_CHART_PATH}/values-staging.yaml \
                            --atomic \
                            --wait
                    """
                }
            }
        }

        stage('Verify Deployment') {
            steps {
                script {
                    // A simple check to see if the release is deployed and running
                    sh "helm status ${HELM_RELEASE_NAME} --namespace ${NAMESPACE}"
                    // You could add more sophisticated checks here, like checking pod status
                }
            }
        }
    }
}

This Jenkinsfile outlines a typical flow. First, it checks out your source code, which includes your Helm charts. Then, it sets up the environment to use a Kubernetes configuration file stored as a Jenkins credential. The core of the deployment is the helm upgrade --install command. This command is idempotent: if the release named my-app-release doesn’t exist in the staging namespace, it will install it; otherwise, it will upgrade the existing release. The --atomic flag is crucial for ensuring that if the upgrade fails, Helm automatically rolls back to the previous working version. --wait makes the pipeline pause until all Kubernetes resources managed by the chart are in a ready state. Finally, a helm status command provides a quick verification.

The problem Helm solves is managing complex Kubernetes applications. Instead of writing dozens of YAML manifests for deployments, services, ingress, configmaps, etc., you can define your application as a single Helm chart. This chart is a collection of templated Kubernetes YAML files, allowing you to parameterize configurations (like image tags, replica counts, resource limits) using a values.yaml file. Jenkins then acts as the orchestrator, fetching the chart (either from a local path in the repo or a remote Helm repository) and instructing Helm to deploy it to your Kubernetes cluster.

Internally, helm upgrade --install does a few things:

  1. Connects to the Kubernetes API: It uses the provided kubeconfig to authenticate and authorize against your cluster.
  2. Renders the Chart: It takes your chart’s templates and combines them with the values from your values.yaml (and any other specified value files). This process fills in the placeholders in your Kubernetes manifests.
  3. Calculates Differences: It compares the desired state (the rendered manifests) with the current state of the release in Kubernetes.
  4. Applies Changes: It applies the necessary Kubernetes API calls (create, update, delete) to make the cluster match the desired state.
  5. Manages Release History: Helm keeps track of deployed releases, allowing for rollbacks.

The exact levers you control are primarily within your values.yaml and the command-line flags passed to helm upgrade. For instance, to change the number of replicas for your application in the staging environment, you’d modify charts/my-app/values-staging.yaml:

replicaCount: 3

image:
  repository: my-docker-registry/my-app
  tag: v1.2.0
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

And the Jenkins pipeline would ensure this chart is deployed with these values.

A common pitfall is managing the Kubernetes context within Jenkins. If your Jenkins agent doesn’t have a kubeconfig file properly configured, Helm won’t be able to talk to your cluster. The credentials('my-kubeconfig-secret') step in the Jenkinsfile assumes you’ve stored your kubeconfig content as a "Secret file" credential in Jenkins. This file needs to contain the necessary cluster, user, and context information. The subsequent sh commands then ensure this file is placed in the expected location ($HOME/.kube/config) and has the correct permissions before Helm is invoked.

The one thing most people don’t know is that Helm’s --wait flag doesn’t just wait for pods to be ready; it waits for all Kubernetes resources defined in the chart to reach a stable, ready state. This includes Deployments, StatefulSets, DaemonSets, Services, Ingresses, PersistentVolumeClaims, and more. If any of these resources get stuck in a pending or error state, the --wait flag will cause the Helm command to time out, and the pipeline will fail, which is usually the desired behavior to catch deployment issues early.

The next concept you’ll likely encounter is managing multiple environments (dev, staging, prod) with different configurations for the same Helm chart, often involving parameterized Jenkins jobs or more sophisticated pipeline logic for environment promotion.

Want structured learning?

Take the full Jenkins course →