Why Package Kubernetes Apps?
Why Package Kubernetes Apps?
You have mastered Kubernetes primitives — Deployments, Services, ConfigMaps, Ingresses, HorizontalPodAutoscalers. Now imagine handing a colleague the task of deploying a single microservice. They need: a Deployment, a Service, a ConfigMap, a Secret reference, an Ingress rule, a PodDisruptionBudget, and a ServiceAccount. That is seven YAML files — before you touch environment-specific values. Multiply by thirty microservices across dev, staging, and production. Welcome to YAML sprawl.
The YAML Sprawl Problem
In the early Kubernetes era, teams managed raw manifests in a k8s/ directory. This approach breaks down along four fault lines:
- Duplication. The same
Deploymentblock is copied for every environment, with minor value differences — image tag, replica count, resource limits. A missed update in one copy is a production incident waiting to happen. - No atomicity.
kubectl apply -f k8s/applies files one at a time, alphabetically. If theConfigMapapply succeeds but theDeploymentfails, you are left in an unknown intermediate state with no built-in rollback. - Versioning is implicit at best. What version of "the app" is running in production? Git commit hashes tie code to images, but they say nothing about which collection of Kubernetes objects represents a known-good configuration.
- Parameterisation is ad-hoc. Teams reach for
sed,envsubst, or custom shell scripts to inject values. These scripts are fragile, untested, and never documented as part of the application interface.
envsubst to inject the image tag into manifests before applying. A developer forgets to export the variable, envsubst replaces the placeholder with an empty string, and kubectl apply silently deploys image: "". The pod crashes immediately, but no one understands why because the command exited zero.
Consider a minimal raw-manifest layout for a single service:
This is already 10 files for one service. With thirty services you manage 300 files, and keeping base changes in sync across overlays is a continuous manual burden. When an engineer asks "what changed between the prod deploy last Tuesday and this Tuesday?", the answer requires diffing dozens of files across multiple directories.
Helm: Kubernetes Package Manager
Helm solves YAML sprawl the same way apt, brew, and npm solved software distribution: it introduces a package format (the chart), a repository system for sharing charts, and a release model that tracks what is deployed in the cluster.
The three core concepts you must internalize from day one:
- Chart — a directory tree of templates and default values that describes a Kubernetes application. Think of it as the source tarball.
- Release — a named, versioned instance of a chart installed in a cluster. You can install the same chart multiple times (once per environment, or once per tenant) and each install is an independent release with its own history.
- Repository — an HTTP server that hosts packaged charts (
.tgzarchives) and anindex.yaml. Artifact Hub (artifacthub.io) is the public registry. Private repos are hosted on Nexus, Artifactory, OCI registries (ECR, GCR, ACR), or plain S3 buckets.
What Helm Actually Does — the Install/Upgrade Loop
Under the hood, helm install and helm upgrade do the following in sequence:
- Load the chart and merge user-supplied values over the defaults.
- Render all templates in
templates/via the Go template engine, producing a set of Kubernetes manifests. - Run pre-install/pre-upgrade hooks (Jobs, shell scripts — covered in lesson 7).
- Apply all manifests to the cluster via the Kubernetes API server.
- Wait for resources to reach the ready state (with
--wait) and roll back if the timeout expires (with--atomic). - Store the rendered manifest set as a release secret in the target namespace (
kubectl get secret -l owner=helm). This is the source of truth for rollbacks.
--atomic, Helm rolls back automatically if the upgrade does not become healthy within the timeout. Google SRE practice calls for every deploy to be reversible within one minute — Helm's rollback mechanism (backed by the stored release secrets) makes that operationally feasible without scripting a manual kubectl apply to a previous manifest snapshot.
Helm vs. the Alternatives
Helm is not the only tool in this space. You will encounter these in production codebases:
- Kustomize — built into
kubectl(kubectl apply -k). Patch-based, no templating language. Excellent for simple overlays; becomes unwieldy when logic is needed (e.g. "enable this sidecar only in prod"). - Kapp + ytt (Carvel) — used heavily at VMware/Tanzu.
yttprovides Python-like logic in YAML;kapptracks resource ownership like Helm releases but without a templating layer. - Jsonnet / Tanka — Grafana Labs' toolchain. Strong typing and reuse for large monorepos, steep learning curve.
- Raw manifests + CI scripts — the most common starting point, and the fastest path to the sprawl problem described above.
Helm wins on ecosystem breadth and operational tooling. The public chart ecosystem (thousands of charts on Artifact Hub) means you install battle-tested infrastructure (Prometheus, cert-manager, Nginx Ingress) in minutes rather than writing and maintaining manifests from scratch. For custom application charts, Helm's templating is expressive enough for 95% of production use cases.
Setting Up Helm
Helm 3 (the current major version, released November 2019) removed the server-side Tiller component that caused RBAC nightmares in Helm 2. Every Helm 3 install is client-only and uses your local kubeconfig credentials directly.
With Helm installed and a kubeconfig pointing at your cluster, you are ready to move from understanding the packaging problem to writing your first chart in lesson 3. Lesson 2 covers consuming existing community charts — the fastest way to build intuition for chart anatomy before authoring your own.