Helm with Jenkins
A comprehensive guide to Helm with Jenkins covering Jenkins pipelines, chart automation, deployment strategies, and best practices for automated Helm workflows.
Jenkins
Pipelines
Automation
Why Jenkins for Helm?
Jenkins remains one of the most widely used CI/CD tools in enterprises. When combined with Helm, it provides a powerful platform for automating Kubernetes deployments. Jenkins offers several advantages for Helm workflows:
- Mature Ecosystem: Thousands of plugins for integration
- Flexibility: Supports any workflow pattern
- Distributed Builds: Scale across multiple agents
- Pipeline as Code: Declarative and scripted pipelines
- Kubernetes Integration: Native support for Kubernetes agents
- Enterprise Features: RBAC, audit logs, and security
Key Concept: Jenkins pipelines can automate the entire Helm chart lifecycle: linting, testing, packaging, signing, and deploying—all triggered by Git events or schedules.
Jenkins Pipeline Basics for Helm
// Jenkinsfile - Basic Helm Pipeline
pipeline {
agent any
environment {
HELM_VERSION = 'v3.14.0'
CHART_DIR = 'charts/my-app'
RELEASE_NAME = 'my-app'
NAMESPACE = 'default'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install Helm') {
steps {
sh '''
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh --version ${HELM_VERSION}
helm version
'''
}
}
stage('Lint') {
steps {
sh 'helm lint ${CHART_DIR}'
}
}
stage('Template') {
steps {
sh 'helm template ${RELEASE_NAME} ${CHART_DIR} --debug'
}
}
stage('Package') {
steps {
sh 'helm package ${CHART_DIR} --destination dist'
}
}
stage('Deploy') {
steps {
sh 'helm upgrade --install ${RELEASE_NAME} ${CHART_DIR} --namespace ${NAMESPACE} --wait --timeout 5m'
}
}
stage('Test') {
steps {
sh 'helm test ${RELEASE_NAME} --timeout 5m'
}
}
}
post {
success {
echo 'Deployment successful!'
}
failure {
echo 'Deployment failed!'
}
always {
cleanWs()
}
}
}
Declarative Pipeline
Simpler, more structured syntax. Recommended for most Helm workflows.
Scripted Pipeline
More flexible, Groovy-based syntax. For complex workflows.
Kubernetes Agents
Dynamic agents that run in Kubernetes pods. Efficient resource usage.
Artifacts
Store packaged charts as Jenkins artifacts for distribution.
Kubernetes Agents for Helm
Jenkins Kubernetes plugin allows dynamic agent provisioning in Kubernetes. This is ideal for Helm workflows as it provides isolated, ephemeral environments for each build.
// Jenkinsfile - Kubernetes Agent
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: helm
image: alpine/helm:3.14.0
command:
- cat
tty: true
- name: kubectl
image: bitnami/kubectl:1.28
command:
- cat
tty: true
- name: docker
image: docker:24.0
command:
- cat
tty: true
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock
'''
}
}
stages {
stage('Lint') {
steps {
container('helm') {
sh 'helm lint charts/my-app'
}
}
}
stage('Package') {
steps {
container('helm') {
sh 'helm package charts/my-app --destination dist'
}
}
}
stage('Build Image') {
steps {
container('docker') {
sh '''
docker build -t myregistry/my-app:${BUILD_NUMBER} .
docker push myregistry/my-app:${BUILD_NUMBER}
'''
}
}
}
stage('Deploy') {
steps {
container('kubectl') {
sh '''
helm upgrade --install my-app charts/my-app \
--namespace default \
--set image.tag=${BUILD_NUMBER} \
--wait --timeout 5m
'''
}
}
}
stage('Test') {
steps {
container('helm') {
sh 'helm test my-app --timeout 5m'
}
}
}
}
}
Kubernetes Agent Benefits:
- Isolated environments for each build
- Automatic scaling of build capacity
- No permanent agents to maintain
- Efficient resource utilization
- Consistent tool versions across builds
Multi-Environment Deployment
// Jenkinsfile - Multi-Environment Deployment
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: helm
image: alpine/helm:3.14.0
command: ['cat']
tty: true
- name: kubectl
image: bitnami/kubectl:1.28
command: ['cat']
tty: true
'''
}
}
parameters {
choice(name: 'ENVIRONMENT', choices: ['dev', 'staging', 'production'], description: 'Target environment')
string(name: 'RELEASE_VERSION', defaultValue: '1.0.0', description: 'Chart version')
booleanParam(name: 'DRY_RUN', defaultValue: false, description: 'Dry run only')
}
environment {
CHART_DIR = 'charts/my-app'
RELEASE_NAME = 'my-app'
VALUES_FILE = "values-${params.ENVIRONMENT}.yaml"
}
stages {
stage('Validate') {
steps {
container('helm') {
sh '''
echo "Validating chart for ${ENVIRONMENT}"
helm lint ${CHART_DIR} -f ${CHART_DIR}/${VALUES_FILE}
'''
}
}
}
stage('Template') {
steps {
container('helm') {
sh '''
echo "Rendering templates for ${ENVIRONMENT}"
helm template ${RELEASE_NAME} ${CHART_DIR} \
-f ${CHART_DIR}/${VALUES_FILE} \
--set image.tag=${RELEASE_VERSION} \
> rendered-${ENVIRONMENT}.yaml
'''
}
}
}
stage('Approve') {
when {
expression { params.ENVIRONMENT == 'production' }
}
steps {
timeout(time: 1, unit: 'HOURS') {
input message: "Deploy to ${params.ENVIRONMENT}?",
ok: 'Deploy',
submitter: 'admin,release-manager'
}
}
}
stage('Deploy') {
when {
expression { !params.DRY_RUN }
}
steps {
container('kubectl') {
sh '''
helm upgrade --install ${RELEASE_NAME} ${CHART_DIR} \
--namespace ${ENVIRONMENT} \
--create-namespace \
-f ${CHART_DIR}/${VALUES_FILE} \
--set image.tag=${RELEASE_VERSION} \
--wait --timeout 10m \
--atomic
'''
}
}
}
stage('Verify') {
when {
expression { !params.DRY_RUN }
}
steps {
container('kubectl') {
sh '''
echo "Verifying deployment in ${ENVIRONMENT}"
kubectl get pods -n ${ENVIRONMENT} -l app=my-app
kubectl get svc -n ${ENVIRONMENT} -l app=my-app
kubectl rollout status deployment/my-app -n ${ENVIRONMENT}
'''
}
}
}
stage('Test') {
when {
expression { !params.DRY_RUN }
}
steps {
container('helm') {
sh 'helm test ${RELEASE_NAME} -n ${ENVIRONMENT} --timeout 5m'
}
}
}
}
post {
success {
slackSend(
channel: '#deployments',
color: 'good',
message: "✅ Deployment successful: ${RELEASE_NAME} to ${ENVIRONMENT} (${RELEASE_VERSION})"
)
}
failure {
slackSend(
channel: '#deployments',
color: 'danger',
message: "❌ Deployment failed: ${RELEASE_NAME} to ${ENVIRONMENT} (${RELEASE_VERSION})"
)
}
}
}
Multi-Environment Best Practices:
- Use environment-specific values files
- Require approval for production deployments
- Use
--atomicfor automatic rollback - Verify deployments after installation
- Run helm tests for each environment
- Send notifications on success/failure
Advanced Pipeline Features
Chart Signing in Jenkins
// Jenkinsfile - Chart Signing
pipeline {
agent any
environment {
GPG_KEY_ID = credentials('gpg-key-id')
GPG_PASSPHRASE = credentials('gpg-passphrase')
}
stages {
stage('Import GPG Key') {
steps {
withCredentials([file(credentialsId: 'gpg-private-key', variable: 'GPG_KEY')]) {
sh '''
gpg --batch --import $GPG_KEY
'''
}
}
}
stage('Package and Sign') {
steps {
sh '''
helm package charts/my-app \
--sign \
--key "${GPG_KEY_ID}" \
--keyring ~/.gnupg/secring.gpg \
--destination dist
'''
}
}
stage('Verify Signature') {
steps {
sh '''
for chart in dist/*.tgz; do
helm verify "$chart"
done
'''
}
}
stage('Publish') {
steps {
sh '''
helm push dist/*.tgz oci://myregistry.azurecr.io/helm
'''
}
}
}
}
Unit Testing in Jenkins
// Jenkinsfile - Unit Testing
pipeline {
agent any
stages {
stage('Install Plugins') {
steps {
sh 'helm plugin install https://github.com/helm-unittest/helm-unittest'
}
}
stage('Unit Test') {
steps {
sh 'helm unittest charts/my-app --debug'
}
post {
always {
junit 'test-results/*.xml'
}
}
}
stage('Validate with Kubeconform') {
steps {
sh '''
wget https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz
tar xf kubeconform-linux-amd64.tar.gz
helm template test charts/my-app | ./kubeconform -strict
'''
}
}
}
}
Security Scanning in Jenkins
// Jenkinsfile - Security Scanning
pipeline {
agent any
stages {
stage('Scan Charts') {
steps {
sh '''
# Install Trivy
wget https://github.com/aquasecurity/trivy/releases/latest/download/trivy_0.48.0_Linux-64bit.tar.gz
tar xf trivy_0.48.0_Linux-64bit.tar.gz
# Scan chart
./trivy config charts/my-app
'''
}
}
stage('Check Secrets') {
steps {
sh '''
# Install gitleaks
wget https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_8.18.0_linux_x64.tar.gz
tar xf gitleaks_8.18.0_linux_x64.tar.gz
# Scan for secrets
./gitleaks detect --source charts/my-app --verbose
'''
}
}
}
}
Jenkins + Helm Best Practices
Pipeline as Code
Store Jenkinsfile in your Git repository alongside charts.
Use Kubernetes Agents
Run builds in ephemeral Kubernetes pods for isolation.
Secure Credentials
Use Jenkins Credentials for registry tokens and GPG keys.
Lint Before Deploy
Always lint and validate charts before deployment.
Atomic Deployments
Use
--atomic for automatic rollback on failure.Notifications
Send build and deployment notifications to Slack/Teams.
Archive Artifacts
Store packaged charts and test results as artifacts.
Security Scans
Scan charts for vulnerabilities and secrets in CI.
// Best practices summary
// 1. Pipeline as Code
// Store Jenkinsfile in Git:
// - charts/my-app/Jenkinsfile
// - Jenkinsfile in repository root
// 2. Use Kubernetes Agents
agent {
kubernetes {
yaml '''...'''
}
}
// 3. Secure Credentials
withCredentials([
string(credentialsId: 'registry-token', variable: 'TOKEN'),
file(credentialsId: 'gpg-key', variable: 'GPG_KEY')
]) {
// Use credentials
}
// 4. Lint Before Deploy
sh 'helm lint charts/my-app'
// 5. Atomic Deployments
sh '''
helm upgrade --install my-app charts/my-app \
--atomic \
--wait \
--timeout 10m
'''
// 6. Notifications
post {
success {
slackSend(channel: '#deployments', color: 'good', message: 'Deployment successful')
}
failure {
slackSend(channel: '#deployments', color: 'danger', message: 'Deployment failed')
}
}
// 7. Archive Artifacts
archiveArtifacts artifacts: 'dist/*.tgz', fingerprint: true
// 8. Security Scans
sh 'trivy config charts/my-app'
sh 'gitleaks detect --source charts/my-app'
Frequently Asked Questions
How do I install Helm in Jenkins?
Use the official install script in a pipeline step:
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash. Or use a Docker image with Helm pre-installed. How do I use Kubernetes agents with Helm?
Use the Jenkins Kubernetes plugin. Define a pod template with Helm and kubectl containers. The agent runs in a pod, providing isolation and scalability.
How do I handle secrets in Jenkins for Helm?
Use Jenkins Credentials to store registry tokens, GPG keys, and Kubernetes configs. Use
withCredentials to securely inject them into pipeline steps. How do I implement multi-environment deployment with Jenkins?
Use parameters to select the environment, environment-specific values files, and input steps for production approval. Deploy to each environment sequentially with verification.
How do I sign Helm charts in Jenkins?
Import your GPG private key from Jenkins Credentials, then use
helm package --sign. Store the key and passphrase as credentials. How do I run helm test in Jenkins?
After deployment, run
helm test <release> --timeout 5m. Check the exit code and fail the pipeline if tests fail. How do I rollback with Jenkins?
Use
helm rollback <release> <revision> in a pipeline step. Or use --atomic during upgrade for automatic rollback on failure. What are the best practices for Jenkins + Helm?
Store Jenkinsfile in Git, use Kubernetes agents, secure credentials, lint before deploy, use atomic deployments, send notifications, archive artifacts, and scan for security issues.
Related Topics
Jenkins provides a powerful, flexible platform for automating Helm chart workflows. Implement these pipelines and best practices to ensure reliable, secure, and efficient Helm deployments.