Skip to content

Module 2.2: Helm Package Manager

Hands-On Lab Available
K8s Cluster intermediate 30 min
Launch Lab ↗

Opens in Killercoda in a new tab

Complexity: [MEDIUM] - Essential tool added to CKAD 2025

Time to Complete: 45-55 minutes

Prerequisites: Module 2.1 (Deployments), understanding of YAML templates


After completing this module, you will be able to:

  • Deploy applications using helm install with custom values and namespace targeting
  • Create value overrides to customize chart behavior for different environments
  • Debug failed Helm releases using helm status, helm history, and rollback operations
  • Explain Helm chart structure including templates, values, and the release lifecycle

Helm is the package manager for Kubernetes. Instead of managing dozens of YAML files, Helm bundles them into “charts” that can be installed, upgraded, and rolled back as a unit. The 2025 CKAD exam added Helm as a required skill.

You’ll encounter questions like:

  • Install a chart from a repository
  • Upgrade a release with new values
  • Rollback a failed release
  • List and inspect releases
  • Create basic chart customizations

The App Store Analogy

Helm is like an app store for Kubernetes. Charts are apps—pre-packaged, tested, and ready to install. Just like you’d install Slack with one click instead of compiling it yourself, Helm lets you install complex applications (databases, monitoring stacks, web servers) with a single command. Values are like app settings—you customize behavior without modifying the app itself.


TermDescription
ChartPackage of Kubernetes resources (like an app)
ReleaseInstalled instance of a chart
RepositoryCollection of charts (like apt repos)
ValuesConfiguration for customizing a chart
RevisionVersion of a release after upgrade/rollback
Chart (template) + Values (config) = Release (running app)
┌─────────────────────────────────────────────────────────┐
│ Helm Workflow │
├─────────────────────────────────────────────────────────┤
│ │
│ Repository Chart Release │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ bitnami │──────▶│ nginx │─────▶│ my-web │ │
│ │ repo │ pull │ chart │install│ release │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
│ │ values │ │ Pods │ │
│ │ .yaml │ │Services │ │
│ └─────────┘ │ConfigMaps│ │
│ └─────────┘ │
└─────────────────────────────────────────────────────────┘

Terminal window
# Add a repository
helm repo add bitnami https://charts.bitnami.com/bitnami
# Add stable repo
helm repo add stable https://charts.helm.sh/stable
# Update repository cache
helm repo update
# List repositories
helm repo list
# Search for charts
helm search repo nginx
helm search repo bitnami/nginx
# Search with versions
helm search repo nginx --versions
Terminal window
# Install with default values
helm install my-release bitnami/nginx
# Install in specific namespace
helm install my-release bitnami/nginx -n production
# Install and create namespace
helm install my-release bitnami/nginx -n production --create-namespace
# Install with custom values file
helm install my-release bitnami/nginx -f values.yaml
# Install with inline values
helm install my-release bitnami/nginx --set replicaCount=3
# Install with multiple value overrides
helm install my-release bitnami/nginx \
--set replicaCount=3 \
--set service.type=NodePort
# Dry-run (see what would be created)
helm install my-release bitnami/nginx --dry-run
# Generate name automatically
helm install bitnami/nginx --generate-name
Terminal window
# List releases in current namespace
helm list
# List in all namespaces
helm list -A
# List in specific namespace
helm list -n production
# List with status
helm list --all
# Filter by status
helm list --failed
helm list --pending
Terminal window
# Show chart info
helm show chart bitnami/nginx
# Show default values
helm show values bitnami/nginx
# Show all info
helm show all bitnami/nginx
# Get release values
helm get values my-release
# Get all release info
helm get all my-release
# Get release manifest (rendered YAML)
helm get manifest my-release
# Get release history
helm history my-release
Terminal window
# Upgrade with new values
helm upgrade my-release bitnami/nginx --set replicaCount=5
# Upgrade with values file
helm upgrade my-release bitnami/nginx -f new-values.yaml
# Upgrade or install if not exists
helm upgrade --install my-release bitnami/nginx
# Reuse existing values and add new ones
helm upgrade my-release bitnami/nginx --reuse-values --set image.tag=1.21

Stop and think: You run helm upgrade my-release bitnami/nginx --set replicaCount=5 without --reuse-values. What happens to all the other custom values you set during the initial install? This is a very common mistake.

Terminal window
# Rollback to previous revision
helm rollback my-release
# Rollback to specific revision
helm rollback my-release 2
# Check history first
helm history my-release
Terminal window
# Uninstall release
helm uninstall my-release
# Uninstall but keep history
helm uninstall my-release --keep-history
# Uninstall from namespace
helm uninstall my-release -n production

Pause and predict: If you pass both a values file with replicaCount: 2 AND use --set replicaCount=5 on the same helm install command, which value wins? Think about why Helm would design it this way.

Values Hierarchy (Lowest to Highest Priority)

Section titled “Values Hierarchy (Lowest to Highest Priority)”
  1. Default values in chart (values.yaml)
  2. Parent chart values
  3. Values file passed with -f
  4. Individual values with --set
my-values.yaml
replicaCount: 3
image:
repository: nginx
tag: "1.21"
pullPolicy: IfNotPresent
service:
type: NodePort
port: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
nodeSelector:
disktype: ssd
Terminal window
# Install with values file
helm install my-release bitnami/nginx -f my-values.yaml
# Multiple values files (later overrides earlier)
helm install my-release bitnami/nginx -f values.yaml -f production.yaml
# Combine file and inline
helm install my-release bitnami/nginx -f values.yaml --set replicaCount=5
Terminal window
# Simple value
--set replicaCount=3
# Nested value
--set image.tag=1.21
# String value (use quotes for special chars)
--set image.repository="my-registry.com/nginx"
# Array value
--set nodeSelector.disktype=ssd
# Multiple values
--set replicaCount=3,service.type=NodePort
# List items
--set ingress.hosts[0].host=example.com

Terminal window
# Add repo
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
# Install with custom values
helm install my-nginx bitnami/nginx \
--set replicaCount=2 \
--set service.type=ClusterIP \
-n web --create-namespace
# Verify
helm list -n web
k get pods -n web
Terminal window
# Check current release
helm list
helm get values my-nginx
# Upgrade
helm upgrade my-nginx bitnami/nginx --set replicaCount=3
# Something goes wrong - rollback
helm history my-nginx
helm rollback my-nginx 1
# Verify
helm list
Terminal window
# See what you're installing
helm show values bitnami/nginx | head -50
# Dry run to see generated manifests
helm install test bitnami/nginx --dry-run | less
# Then install
helm install my-nginx bitnami/nginx

my-chart/
├── Chart.yaml # Chart metadata
├── values.yaml # Default configuration
├── charts/ # Dependency charts
├── templates/ # Kubernetes manifests
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── _helpers.tpl # Template helpers
│ └── NOTES.txt # Post-install notes
└── README.md

You won’t create charts in the CKAD exam, but understanding structure helps with debugging.


What would happen if: You run helm install my-app bitnami/nginx in the default namespace, then later run helm list in the production namespace. Would you see my-app in the output? Why does this matter during the exam?

Terminal window
# Release stuck in pending-install
helm list --pending
helm uninstall stuck-release
# See what's wrong
helm get manifest my-release | k apply --dry-run=server -f -
# Debug template rendering
helm template my-release bitnami/nginx --debug
# Check release status
helm status my-release
Terminal window
# See rendered templates
helm template my-release bitnami/nginx > rendered.yaml
# Validate without installing
helm install my-release bitnami/nginx --dry-run --debug
# Get notes (post-install instructions)
helm get notes my-release

  • Helm 3 removed Tiller. Helm 2 required a server-side component (Tiller) with cluster-admin privileges. Helm 3 runs entirely client-side, using your kubeconfig permissions.

  • helm upgrade --install is idempotent—it installs if the release doesn’t exist, or upgrades if it does. Great for CI/CD pipelines.

  • Helm stores release data as Secrets (default) or ConfigMaps. Each revision is a separate Secret, enabling rollback to any previous state.


MistakeWhy It HurtsSolution
Forgetting helm repo updateInstall old chart versionsAlways update before installing
Wrong namespaceRelease in default namespaceUse -n namespace consistently
--set typosValues not appliedUse --dry-run to verify
Forgetting --reuse-valuesUpgrade resets to defaultsAdd flag when only changing some values
Not checking history before rollbackRoll back to wrong versionRun helm history first

  1. Your CI/CD pipeline needs to deploy a chart that may or may not already be installed in the cluster. It should install on first run and upgrade on subsequent runs, without error. What single Helm command handles both cases?

    Answer Use `helm upgrade --install my-release bitnami/nginx`. The `--install` flag makes the command idempotent -- it installs if the release doesn't exist and upgrades if it does. This is the standard pattern for CI/CD pipelines because it eliminates the need for conditional logic to check whether a release exists. Add `--atomic` to automatically roll back if the upgrade fails, ensuring the pipeline never leaves a broken release behind.
  2. A teammate upgraded a Helm release with helm upgrade my-app bitnami/nginx --set replicaCount=5 but now reports that the service.type they set during initial install has reverted to the chart default LoadBalancer instead of their custom ClusterIP. What happened and how do they fix future upgrades?

    Answer Without `--reuse-values`, `helm upgrade` resets all values to chart defaults except those explicitly passed in the current command. Only `replicaCount=5` was set, so `service.type` reverted to the default. Fix with: `helm rollback my-app` to restore the previous state, then `helm upgrade my-app bitnami/nginx --reuse-values --set replicaCount=5`. The `--reuse-values` flag carries forward all values from the previous revision. Better yet, maintain a values file with all customizations and always pass it: `helm upgrade my-app bitnami/nginx -f values.yaml --set replicaCount=5`.
  3. During the CKAD exam, you’re told to “find what values the release web-prod is using in the frontend namespace.” You run helm get values web-prod and get “release not found.” The release definitely exists. What’s wrong?

    Answer Helm releases are namespace-scoped. You need to specify the namespace: `helm get values web-prod -n frontend`. By default, Helm looks in the current kubectl context namespace. This is a common exam pitfall -- always include `-n ` when working with Helm releases outside the default namespace. Use `helm list -A` to find releases across all namespaces when you're not sure where a release lives.
  4. You installed a Helm chart but the pods are crash-looping. You need to inspect the exact Kubernetes manifests that Helm generated to see if the configuration is correct. How do you view the rendered YAML without uninstalling and reinstalling?

    Answer Run `helm get manifest web-prod` to see the exact rendered Kubernetes YAML that was applied. This shows every resource (Deployments, Services, ConfigMaps, etc.) as they were sent to the API server. For debugging before deploying, use `helm template my-release bitnami/nginx -f values.yaml` to render templates locally without contacting the cluster, or `helm install my-release bitnami/nginx --dry-run --debug` to simulate the install with server-side validation.

Task: Complete Helm workflow with a real chart.

Setup:

Terminal window
# Add repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

Part 1: Inspect and Install

Terminal window
# See available values
helm show values bitnami/nginx | head -30
# Install with custom values
helm install web bitnami/nginx \
--set replicaCount=2 \
--set service.type=ClusterIP
# Verify installation
helm list
k get pods -l app.kubernetes.io/instance=web

Part 2: Upgrade

Terminal window
# Upgrade replicas
helm upgrade web bitnami/nginx --reuse-values --set replicaCount=3
# Check history
helm history web
# Verify pods
k get pods -l app.kubernetes.io/instance=web

Part 3: Rollback

Terminal window
# Rollback to revision 1
helm rollback web 1
# Verify reverted
helm get values web
k get pods -l app.kubernetes.io/instance=web

Part 4: Cleanup

Terminal window
helm uninstall web

Drill 1: Repository Management (Target: 2 minutes)

Section titled “Drill 1: Repository Management (Target: 2 minutes)”
Terminal window
# Add bitnami repo
helm repo add bitnami https://charts.bitnami.com/bitnami
# Update
helm repo update
# Search for mysql
helm search repo mysql
# List repos
helm repo list

Drill 2: Basic Install (Target: 2 minutes)

Section titled “Drill 2: Basic Install (Target: 2 minutes)”
Terminal window
# Install nginx
helm install drill2 bitnami/nginx
# List releases
helm list
# Check status
helm status drill2
# Cleanup
helm uninstall drill2

Drill 3: Install with Values (Target: 3 minutes)

Section titled “Drill 3: Install with Values (Target: 3 minutes)”
Terminal window
# Create values file
cat << 'EOF' > /tmp/values.yaml
replicaCount: 2
service:
type: ClusterIP
EOF
# Install with values file
helm install drill3 bitnami/nginx -f /tmp/values.yaml
# Verify values applied
helm get values drill3
# Cleanup
helm uninstall drill3

Drill 4: Upgrade and Rollback (Target: 4 minutes)

Section titled “Drill 4: Upgrade and Rollback (Target: 4 minutes)”
Terminal window
# Install
helm install drill4 bitnami/nginx --set replicaCount=1
# Upgrade
helm upgrade drill4 bitnami/nginx --set replicaCount=3
# Check history
helm history drill4
# Rollback
helm rollback drill4 1
# Verify
helm get values drill4
# Cleanup
helm uninstall drill4

Drill 5: Namespace Operations (Target: 3 minutes)

Section titled “Drill 5: Namespace Operations (Target: 3 minutes)”
Terminal window
# Install in new namespace
helm install drill5 bitnami/nginx -n helm-test --create-namespace
# List in namespace
helm list -n helm-test
# Get pods in namespace
k get pods -n helm-test
# Cleanup
helm uninstall drill5 -n helm-test
k delete ns helm-test

Drill 6: Complete Scenario (Target: 6 minutes)

Section titled “Drill 6: Complete Scenario (Target: 6 minutes)”

Scenario: Deploy a production-ready nginx.

Terminal window
# 1. Create values file
cat << 'EOF' > /tmp/prod-values.yaml
replicaCount: 3
service:
type: NodePort
nodePorts:
http: 30080
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
EOF
# 2. Dry-run first
helm install prod-web bitnami/nginx -f /tmp/prod-values.yaml --dry-run
# 3. Install
helm install prod-web bitnami/nginx -f /tmp/prod-values.yaml
# 4. Verify
helm list
helm get values prod-web
k get pods -l app.kubernetes.io/instance=prod-web
# 5. Upgrade with more replicas
helm upgrade prod-web bitnami/nginx -f /tmp/prod-values.yaml --set replicaCount=5
# 6. Something wrong - rollback
helm rollback prod-web 1
# 7. Cleanup
helm uninstall prod-web

Module 2.3: Kustomize - Customize Kubernetes resources without templates.