How to Package an App as a Helm Chart
A Helm chart packages your Kubernetes manifests into one reusable, parameterized unit you install with a single command. Here's the chart structure, how templating works, and how to lint and package it.
What a Helm chart is
A chart bundles your Kubernetes YAML (Deployment, Service, etc.) into a versioned, templated package. Instead of hand-editing manifests per environment, you set values and Helm renders the YAML. One command installs the whole app.
The chart layout
charts/api/
Chart.yaml # metadata: name, version, appVersion
values.yaml # default config (image, replicas, ports...)
templates/
deployment.yaml # templated manifests
service.yaml
_helpers.tpl # reusable template snippets
Chart.yaml - identity + versions:
apiVersion: v2
name: api
description: A reusable Helm chart for the API service.
type: application
version: 0.1.0 # the chart version
appVersion: "1.0.0" # the app version it deploys
values.yaml - the knobs users override:
image:
repository: ghcr.io/example/api
tag: latest
replicaCount: 2
service:
port: 80
Templating the manifests
Templates pull from .Values (values.yaml) and .Chart (Chart.yaml):
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
_helpers.tpl holds snippets you reuse (like a standard label block) via {{ include }}.
Validate before you ship
helm lint ./charts/api # catch schema/template errors
helm template ./charts/api # render the YAML locally to eyeball it
helm package ./charts/api # produce api-0.1.0.tgz to publish
Then anyone installs it with helm install api ./charts/api and overrides values with
--set image.tag=v2 or a custom values.yaml.
Why charts beat raw manifests
- One install command instead of
kubectl applyacross many files. - Parameterized - same chart, different values per environment.
- Versioned + upgradable/rollbackable (
helm upgrade/helm rollback).
Environments, upgrades, and rollbacks
One chart, many environments: keep defaults in values.yaml and override per environment with a second file - helm upgrade --install myapp . -f values-prod.yaml. Use --set image.tag=1.4.2 for one-off overrides in CI.
helm upgrade --install is the command you actually run in a pipeline: it installs the release if it is new and upgrades it in place if it exists, so deploys are idempotent. If a release goes bad, helm rollback myapp 1 reverts to a previous revision in seconds - Helm keeps a history you can list with helm history myapp.
Version two things in Chart.yaml: bump version whenever the templates change, and set appVersion to the application image you ship. Keeping them distinct tells consumers whether the packaging or the app changed. Run helm lint and helm template in CI so a broken chart never reaches a cluster.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Authoring Chart.yaml + values.yaml
- Templating manifests with .Values and .Chart
- Validating with helm lint / template / package
FAQ
What files make up a Helm chart?
Chart.yaml (name + versions), values.yaml (default config), and a templates/ directory of templated Kubernetes manifests, often with a _helpers.tpl for reusable snippets.
How do I validate a Helm chart?
Run helm lint to catch errors, helm template to render the YAML locally and inspect it, then helm package to build the distributable .tgz.
What's the difference between version and appVersion in Chart.yaml?
version is the chart's own version (bumped when you change the chart); appVersion is the version of the application the chart deploys.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →