Build a Helm chart from zero (without `helm create`)
Every file in a Helm chart, explained. Deploy your first real chart in 30 minutes without copy-pasting a boilerplate.
helm create myapp generates 15 files you don’t understand and never touch. This guide builds a Helm chart from ONE file up so you actually know what each piece does.
What Helm actually is
Helm = the package manager for Kubernetes. It bundles your K8s YAMLs into a reusable “chart” with configurable values.
Without Helm: 10 YAML files, hard-coded values, painful to reuse.
With Helm: 1 chart, values.yaml, install with helm install.
The minimum viable Helm chart (4 files)
myapp/
├── Chart.yaml ← identity of the chart
├── values.yaml ← default configurable values
└── templates/
├── deployment.yaml ← K8s Deployment with {{ placeholders }}
└── service.yaml ← K8s Service with {{ placeholders }}
That’s it. Four files. A real Helm chart.
Here’s what the actual folder looks like after running helm create, along with the Chart.yaml metadata Helm auto-generates:
File 1 — Chart.yaml
apiVersion: v2
name: myapp
description: My first Helm chart
type: application
version: 0.1.0 # chart version
appVersion: "1.0.0" # your app's version
Helm reads this to know what your chart is called + what version.
File 2 — values.yaml
image:
repository: nginx
tag: latest
pullPolicy: IfNotPresent
replicaCount: 2
service:
type: ClusterIP
port: 80
These are the DEFAULTS. Users of your chart override them via --set or their own values file.
File 3 — templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
Notice {{ .Values.replicaCount }} — that’s a Go template placeholder. At install time, Helm replaces it with the value from values.yaml.
Special values Helm gives you:
.Release.Name— the name you chose when installing (helm install my-app ...).Chart.Name— from Chart.yaml (myapp).Values.xxx— anything from values.yaml
File 4 — templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: 80
selector:
app: {{ .Release.Name }}
Install your chart
From the folder containing myapp/:
helm install my-app ./myapp
Verify:
kubectl get deployments,services
You’ll see my-app created:
Override values at install time
helm install my-app ./myapp --set replicaCount=5 --set image.tag=1.25
Or use a custom values file:
helm install my-app ./myapp -f custom-values.yaml
Upgrade the release
Change values in values.yaml → run:
helm upgrade my-app ./myapp
Helm diffs the current release vs new manifests, applies changes.
Rollback if things break
helm history my-app # see revisions
helm rollback my-app 2 # revert to revision 2
Helm keeps every deploy as a K8s Secret. You can roll back weeks later.
The full “real” Helm chart adds
templates/_helpers.tpl— reusable template helpers (naming, labels)templates/ingress.yaml— Ingress if neededtemplates/serviceaccount.yaml— ServiceAccounttemplates/hpa.yaml— auto-scaling.helmignore— files to exclude from chart package
But start with the 4 files above. Add complexity when you actually need it.
Publishing your chart
Turn your myapp/ folder into a .tgz:
helm package myapp
Upload to a chart repo (GitHub Pages works free, or ChartMuseum).
Users install:
helm repo add myrepo https://your-github-pages.com
helm install my-app myrepo/myapp
Why Helm actually helps
- Reuse: same chart deployed 10 times with different values
- Version control: upgrade + rollback with one command
- Distribution: share your chart, others install with 2 commands
- Templating: loops, conditionals, helpers for complex apps
Common mistakes
- Committing
values.yamlwith secrets — never. Use--setor external secrets - Not versioning your chart — bump
versionin Chart.yaml on every change - Overusing templates — if you have zero conditionals, plain YAML works fine
Helm becomes worth it when you deploy the same app to 3+ environments or share your app with others.
DevOps YAML Pack
36 production-ready configs — Kubernetes, Docker Compose, GitHub Actions, Terraform, Helm, Ansible. Every file heavily commented. Copy, paste, ship.
Get the pack — ₹499 →