
September 09, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
You need repeatable Kubernetes deploys without copying YAML by hand. That is exactly why teams write your first Helm chart before production traffic hits the cluster. Helm packages manifests into a versioned unit you install, upgrade, and roll back with one command. If you already ship Laravel apps with Deployer on bare metal, this guide bridges that workflow to Kubernetes basics using a small PHP-FPM + Nginx example you can adapt to any web stack.
helm create my-app, edit Chart.yaml and values.yaml, add Go-template manifests under templates/, then install with helm install my-release ./my-app and upgrade with helm upgrade.What do you need before you write your first Helm chart?
Helm 3 is the current line. It talks to the Kubernetes API through your kubeconfig. You do not need Tiller anymore. That change alone removed most Helm 2 security headaches.
Install these before you scaffold anything:
- kubectl — configured against a reachable cluster (minikube, kind, AKS, EKS, or a staging VPS)
- Helm 3 — the CLI from the official project (Helm install docs)
- A container image pushed to a registry your cluster can pull
- Basic comfort with Deployment, Service, and Ingress objects
On Ubuntu 24.04, a typical install looks like this:
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
kubectl cluster-info
If you manage servers for clients the way I do with Linux system administration, keep Helm on your laptop or CI runner. Do not install it on every app server. Kubernetes nodes pull images; they do not need the Helm binary unless you deploy from inside the cluster (rare for small teams).
How do you scaffold a new Helm chart from scratch?
Start with the generator. It creates a sensible folder layout you trim down. Do not hand-write every file on day one.
Create the chart directory
mkdir -p ~/charts && cd ~/charts
helm create demo-web
cd demo-web
tree .
You will see Chart.yaml, values.yaml, a templates/ folder, and a charts/ subfolder for dependencies. Delete what you do not need. A minimal web app rarely needs HPA, ServiceAccount boilerplate, or the default nginx example on first pass.
Edit Chart.yaml metadata
apiVersion: v2
name: demo-web
description: Demo web app Helm chart
type: application
version: 0.1.0
appVersion: "1.0.0"
version tracks chart changes. appVersion tracks the application release inside the container. Bump version on every chart edit you ship. CI pipelines often enforce semver here. Read Helm charts explained for how packaging differs from a raw manifest folder.
Define values.yaml defaults
Values are your configuration surface. Keep secrets out of this file. Use Kubernetes Secrets or an external secret manager instead.
replicaCount: 2
image:
repository: registry.example.com/demo-web
tag: "1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: true
className: nginx
hosts:
- host: demo.example.com
paths:
- path: /
pathType: Prefix
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
Paste that into a JSON formatter only when you convert between JSON and YAML for CI. Helm itself expects YAML.
How do you write Helm templates for Deployment, Service, and Ingress?
Templates are standard Kubernetes manifests with Go template directives. Helm renders them at install time. The dot (.) refers to the values tree passed in.
Deployment template
Save this as templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "demo-web.fullname" . }}
labels:
{{- include "demo-web.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "demo-web.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "demo-web.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 8080
resources:
{{- toYaml .Values.resources | nindent 12 }}
Notice include helpers in templates/_helpers.tpl. Keep them. They prevent label drift across objects. That drift breaks selectors silently.
Service and Ingress
Add templates/service.yaml exposing port 80 to pod port 8080. Wrap Ingress in a values guard so staging clusters without an ingress controller still work:
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "demo-web.fullname" . }}
spec:
ingressClassName: {{ .Values.ingress.className }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "demo-web.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
Run helm template demo . locally. It prints rendered YAML without touching the cluster. That command saves hours when a template syntax error would otherwise fail mid-deploy. For larger teams, pair this with Azure Pipelines CI/CD or CircleCI pipelines that lint charts on every push.
How do you install, upgrade, and roll back your first Helm release?
A release is a named install of a chart in a namespace. One chart can produce many releases (staging, QA, prod).
Install the chart
- Create a namespace:
kubectl create namespace demo - Dry-run first:
helm install demo-release . -n demo --dry-run --debug - Install for real:
helm install demo-release . -n demo - Verify:
helm list -n demoandkubectl get pods -n demo
Override values without editing files:
helm install demo-release . -n demo \
--set replicaCount=3 \
--set image.tag=1.0.1 \
-f values-staging.yaml
Upgrade and rollback
Ship a new image tag by bumping values.yaml and running upgrade:
helm upgrade demo-release . -n demo --set image.tag=1.0.2
helm history demo-release -n demo
helm rollback demo-release 1 -n demo
Each upgrade creates a revision. Rollback re-applies a prior revision's rendered manifests. That is the operational win over plain kubectl apply folders. On a booking platform like Adventure Third Pole Trek, zero-downtime deploys still depend on readiness probes and rolling update strategy inside the Deployment template—not Helm alone.
How does Helm compare to raw YAML and Kustomize?
Teams often ask whether Helm is worth the template layer. The honest answer depends on release count and environment variance.
| Approach | Best for | Upgrade / rollback | Learning curve | Package sharing |
|---|---|---|---|---|
| Raw YAML + kubectl | One app, one cluster, few envs | Manual or scripted | Low | Copy-paste repos |
| Kustomize | Overlay patches per env | Git-based, no built-in history | Medium | Base + overlay folders |
| Helm charts | Many envs, shared apps, CI pipelines | Built-in revision history | Medium–high | Chart repos, OCI registries |
Kustomize ships inside kubectl. It excels when differences are patch-shaped ("add three replicas in prod"). Helm excels when differences are parameter-shaped ("image tag, host name, replica count"). Many production setups use both: a Helm chart with environment-specific values files. If you deploy to Azure Kubernetes Service, Helm integrates cleanly with pipeline tasks and OCI artifact storage.
For AI-assisted YAML generation, see using AI to write Terraform and Kubernetes YAML. Always run helm template and kubeconform or helm lint before merging. Models hallucinate API versions often.
What mistakes break your first Helm chart in production?
These show up on real clusters, not just tutorials.
Hard-coded names instead of helpers
Never set metadata.name: demo-web literally. Use {{ include "demo-web.fullname" . }}. Otherwise a second release in the same namespace collides.
Secrets in values.yaml committed to Git
Store DB passwords in Kubernetes Secrets. Reference them from templates with secretKeyRef. For enterprise clients, wire enterprise application development practices: sealed-secrets, External Secrets Operator, or cloud vault integration.
Missing resource limits
Without limits, one pod can starve a node. Set requests and limits in values. Tune them from actual usage, not guesses.
Forgetting hooks and CRD order
Database migration Jobs often need helm.sh/hook: pre-install,pre-upgrade. Custom Resource Definitions must install before controllers that consume them. Read the Kubernetes objects overview when ordering confuses you.
Skipping lint and dry-run in CI
helm lint .
helm template demo-release . -f values-staging.yaml | kubeconform -summary -
Add that step beside unit tests. A broken Ingress template should fail the pipeline, not the production namespace. Support and maintenance contracts get expensive when every deploy is a manual kubectl rescue.
Subcharts before you need them
Do not split database and app into subcharts on day one. Add a charts/postgresql dependency only when multiple apps share it. Until then, one flat chart stays easier to debug. Validate regex in host patterns with a regex tester if you template hostnames dynamically.
When admission policies enforce labels or security contexts, your chart must comply. See admission controllers and webhooks for what cluster admins inject automatically.
How do you publish and reuse your Helm chart?
Once the chart works locally, package it for teammates and CI.
helm package .
helm push demo-web-0.1.0.tgz oci://registry.example.com/charts
Helm 3 supports OCI registries alongside classic chart repos. Tag chart versions the same way you tag Docker images. Document required values in a README inside the chart folder.
On client projects I treat chart repos like Git deploy branches. Staging installs chart version 0.3.x. Production pins an exact patch after QA passes. That mirrors Deployer release symlinks I use on PHP sites—predictable, reversible change.
If your app exposes a REST API, keep deploy config near API versioning docs. API-first development workflow pairs well with chart-per-service repos in larger systems built through custom software development.
For a broader platform picture, browse the portfolio of production systems. Charts are plumbing. The business outcome is still uptime and safe releases.
Key Takeaways
- Run
helm create, trim boilerplate, and keep helpers in_helpers.tplfor consistent names and labels. - Put all environment differences in
values.yamlor-foverride files—never hard-code prod hostnames in templates. - Always
helm lintandhelm template --debugbeforehelm installto catch render errors early. - Use
helm upgradefor releases andhelm rollbackwhen a revision misbehaves—revision history is Helm's core advantage. - Keep secrets out of Git; reference Kubernetes Secret objects from templates instead.
- Package charts with
helm packageand push to an OCI registry once staging proves the chart stable.
People Also Ask
Do I need Helm if I already use kubectl apply?
Not always. A single static manifest folder works for hobby projects. Helm pays off when you manage multiple environments, share charts across services, or need one-command rollbacks. The template plus values split removes copy-paste YAML drift.
What is the difference between a Helm chart and a Helm release?
The chart is the package—the files in your repo. A release is a named instance of that chart running in a cluster namespace. Installing the same chart twice with different release names creates two independent sets of resources.
Can Helm deploy Laravel or PHP applications?
Yes. Build a Docker image with PHP-FPM or Octane, point the chart Deployment at that image, and front it with Nginx in the same pod or a sidecar. Helm does not care about the language. It renders Kubernetes objects. Your Dockerfile and health checks matter more than the chart tool.
Is Helm still maintained in 2026?
Yes. Helm 3 remains the standard packaging layer for Kubernetes applications. The CNCF graduated project ships regular releases. OCI support and security signing continue to improve. Check the official site for the current stable version before pinning CI images.
Ship your first chart, then automate the pipeline
You now have the full path to write your first Helm chart: scaffold with helm create, configure values, template Deployment and Ingress resources, install, upgrade, and roll back with confidence. Start on a staging cluster, wire lint into CI, and only then promote the same chart to production with pinned versions. If you want help moving a Laravel or multi-service app from VPS deploys to Kubernetes, contact us or explore web development services for a practical migration plan.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

