Skip to content
DevOps devops orchestration 5 min read

Packaging with Helm

Once you start running real apps on Kubernetes, you quickly notice a problem: every app needs a pile of YAML files (Deployment, Service, ConfigMap, Secret, Ingress, and more), and most of those files look almost the same except for a few values like the image name or the number of replicas. Copy-pasting and hand-editing all that YAML is slow and error-prone. Helm is the package manager for Kubernetes — it bundles all those YAML files into a single reusable, versioned package called a chart, so you can install a whole application with one command. Think of it like apt on Ubuntu: instead of compiling software by hand, you run apt install and get a ready-to-run package. Helm does the same thing, but for Kubernetes apps.

What problem does Helm solve?

A Kubernetes app is rarely one file. A typical database deployment needs:

  • a Deployment or StatefulSet (which Pods to run)
  • a Service (a stable network address for those Pods)
  • a ConfigMap (non-secret settings)
  • a Secret (passwords and keys)
  • a PersistentVolumeClaim (storage that survives restarts)

If you manage these by hand, you copy them between projects and edit the same fields over and over. When you need a staging copy with a different password and smaller resources, you copy everything again. That copy-pasting is where mistakes creep in — a forgotten label, a mismatched name, a wrong namespace.

Helm replaces copy-pasting with templating (writing the YAML once with blanks, then filling the blanks from a values file). You define the shape of the YAML once, and supply the changing values separately. One chart can produce a dev install, a staging install, and a production install — same templates, different values.

Key Helm concepts

TermWhat it means
ChartA package: a folder (or .tgz archive) of templated Kubernetes YAML plus metadata.
values.yamlThe default settings for a chart (image tag, replicas, passwords, sizes). You override these at install time.
ReleaseOne installed instance of a chart in your cluster. You can install the same chart many times under different release names.
RepositoryA web server hosting many charts, added with helm repo add. Like an apt repository.
TemplateA YAML file with placeholders (e.g. {{ .Values.image.tag }}) that Helm fills in to produce final Kubernetes manifests.

When to use Helm: any time you deploy off-the-shelf software (databases, message queues, monitoring), or any time your own app’s YAML differs only by environment. When NOT to use it: for a tiny one-Pod experiment, plain kubectl apply -f is simpler and you don’t need the extra layer.

Installing Helm on Ubuntu

Helm is a single command-line tool that runs on your machine and talks to your cluster using your existing kubectl config. On Ubuntu 22.04 / 24.04 LTS, install it from the official apt repository:

curl https://baltocdn.com/helm/signing.asc | sudo gpg --dearmor -o /usr/share/keyrings/helm.gpg
sudo apt-get install -y apt-transport-https
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install -y helm

Check the install:

helm version

Output:

version.BuildInfo{Version:"v3.16.2", GitCommit:"13654a5...", GitTreeState:"clean", GoVersion:"go1.22.7"}

Helm 3 (the current major version) has no server-side component. Older guides mention “Tiller”, a pod that ran in your cluster — that was Helm 2 and it was a real security risk because it held cluster-wide permissions. Helm 3 removed it entirely. If a tutorial tells you to helm init or install Tiller, it is out of date — ignore it.

Installing a chart: a PostgreSQL database

Let’s install a real application — a PostgreSQL database from the Bitnami repository. First, add the repository and refresh the local index:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

Output:

"bitnami" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
Update Complete. ⎈Happy Helming!⎈

Before installing, look at what settings the chart exposes. The helm show values command prints the chart’s full values.yaml:

helm show values bitnami/postgresql | head -n 40

Now install it. We give the release the name mydb and override two values: the database password and the storage size. The --set flag overrides one value inline:

helm install mydb bitnami/postgresql \
  --set auth.postgresPassword=ChangeMe123 \
  --set primary.persistence.size=8Gi

Output:

NAME: mydb
LAST DEPLOYED: Mon Jun 15 10:42:01 2026
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
PostgreSQL can be accessed via port 5432 on the following DNS name from within your cluster:
    mydb-postgresql.default.svc.cluster.local

Confirm the release and the Pods it created:

helm list
kubectl get pods

Output:

NAME    NAMESPACE  REVISION  STATUS    CHART              APP VERSION
mydb    default    1         deployed  postgresql-16.2.1  17.4.0

NAME              READY   STATUS    RESTARTS   AGE
mydb-postgresql-0 1/1     Running   0          48s

Using a values file instead of many —set flags

For more than a couple of overrides, put them in a file. This is cleaner, reviewable in Git, and reusable across environments. Create mydb-values.yaml:

auth:
  postgresPassword: ChangeMe123
  database: appdb
primary:
  persistence:
    size: 8Gi
  resources:
    requests:
      cpu: 250m
      memory: 256Mi

Install (or change an existing release) with the file:

helm upgrade --install mydb bitnami/postgresql -f mydb-values.yaml

helm upgrade --install is the safe everyday command: it installs the release if it does not exist, or updates it in place if it does. Each change bumps the revision number, so you can roll back:

helm rollback mydb 1

Never put real passwords in a values file that you commit to Git. Use Kubernetes Secrets, a secrets manager, or --set from an environment variable at install time. A password in your repo history is a password leaked forever.

When you are done, remove everything the chart created with one command:

helm uninstall mydb

Best Practices

  • Use helm upgrade --install everywhere (including first install) so the same command works in scripts and CI/CD.
  • Keep a values file per environment (values-dev.yaml, values-prod.yaml) under version control, but keep secrets out of those files.
  • Pin the chart version with --version so installs are reproducible and don’t silently upgrade.
  • Run helm template ./mychart or helm install --dry-run --debug to preview the rendered YAML before touching the cluster.
  • Use helm rollback instead of editing live resources by hand — it keeps your release history honest.
  • Read the chart’s NOTES output and helm show values before installing; defaults are often not what you want for production.
Last updated June 15, 2026
Was this helpful?