If you're reading this blog I'd guess you're familiar with Helm and want to utilize if effectively. In case you aren't, I got you. Helm is a powerful packaging tool that help scale the Kubernetes manifest in a structured manner. I have highlighted the importance of Helm in some of my previous blogs and I'm covering some extra bits in this one. Just want my DevOps Engineer to not miss out on this simple yet effective tool. Do check my previous releases on Helm on my profile.
1) Executive Summary
Helm charts can be a blessing or a mess — depending on how disciplined your structure is. In this post, I’ll show how to design charts that scale: clean folder layout, validated values, reusable helpers, and consistent environment overlays.
Think of this as the point where “copy-pasted YAML” evolves into a templatized, validated, and reusable release artifact.
2) Prereqs
You should already be comfortable with:
-
kubectlbasics and manifests (Deployment,Service,Ingress). -
helm install,upgrade, anduninstall. - YAML indentation and Go template syntax (
{{ .Values.image.repository }}).
If not, go back to Post 5 where we replaced Ingress with Gateway API — that’s the level of confidence you need.
3) Concepts
a) Chart layout & sane defaults
A Helm chart is just structured YAML + templates + metadata.
Default layout:
mychart/
├── Chart.yaml
├── values.yaml
├── values.schema.json
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── _helpers.tpl
└── charts/
Why it matters:
-
Chart.yamldefines metadata and dependencies. -
values.yamlholds configuration knobs (default values). -
templates/defines the actual Kubernetes manifests. -
_helpers.tplhosts shared snippets like names and labels.
Decision cue:
If you can’t read your chart structure at a glance — it’s probably too magical. Keep it explicit.
b) values.schema.json validation
This file enforces structure and type-checking for your values.yaml.
Without it, Helm silently ignores typos — your app may deploy with wrong configs.
Before → After
Before:
# values.yaml
image:
repo: nginx
tag: latest # typo: should be 'repository'
After:
// values.schema.json
{
"$schema": "https://json-schema.org/draft-07/schema#",
"properties": {
"image": {
"type": "object",
"properties": {
"repository": { "type": "string" },
"tag": { "type": "string" }
},
"required": ["repository"]
}
}
}
Now, helm lint will fail fast if someone mis-keys a field.
Decision cue:
If multiple people edit values.yaml, a schema is mandatory.
c) Helpers/partials & library charts
Avoid repetition. Use helpers (_helpers.tpl) for common names, labels, and annotations.
# _helpers.tpl
{{- define "app.fullname" -}}
{{ printf "%s-%s" .Release.Name .Chart.Name }}
{{- end -}}
Then reuse it:
metadata:
name: {{ include "app.fullname" . }}
When multiple services share common patterns (e.g., serviceaccount.yaml, probes, labels), extract them into a library chart.
Library charts have no manifests — just helpers and templates that other charts can import.
Decision cue:
When you copy the same YAML block in more than two charts — extract it into a helper or library.
d) Environment overlays strategy
Instead of maintaining values-dev.yaml, values-stg.yaml, and values-prod.yaml in chaos, organize by overlays:
values/
base.yaml
dev.yaml
stg.yaml
prod.yaml
Each extends the base:
helm install myapp . -f values/base.yaml -f values/prod.yaml
Best practice:
- Keep common knobs in
base.yaml. - Only override what truly differs across envs.
- Avoid env-specific conditionals in templates (
if .Values.env == "prod"→ smells).
4) Mini-Lab — From Raw Manifests to Chart
- Start with raw YAML:
kubectl create deploy web --image=nginx --dry-run=client -o yaml > deployment.yaml
kubectl expose deploy web --port=80 --dry-run=client -o yaml > service.yaml
- Create a chart:
helm create webapp
-
Move manifests: replace defaults with your generated files under
templates/. -
Add schema validation:
Create
values.schema.jsonto validate image & replica fields. -
Extract helpers:
Move naming logic to
_helpers.tpl. - Test locally:
helm template webapp .
helm lint webapp
helm install webapp . --dry-run
Result: a reusable, validated chart with clear structure.
5) Cheatsheet
| Command | Purpose |
|---|---|
helm show values <chart> |
Display default values |
helm lint . |
Validate chart + schema |
helm template . |
Render YAML locally |
helm upgrade --install <release> . -f values/prod.yaml |
Idempotent deploy |
| Schema tip: | Run jq . values.schema.json to quickly validate structure |
6) Pitfalls
| Pitfall | Why It Hurts | Fix |
|---|---|---|
| Values drift | Each team edits values.yaml differently. |
Enforce schema + overlays. |
| Over-templating | Logic explodes inside YAML. | Keep logic minimal; pre-compute in CI/CD. |
| No library reuse | Duplication everywhere. | Move shared blocks to _helpers.tpl or library charts. |
| Missing schema | Silent deployment errors. | Add values.schema.json early. |
7) Wrap-up — Next: Shipping Charts with Confidence: Lint, Tests, Diff, OCI & Provenance (Post 7)
We now have clean, validated, reusable Helm charts that can be tested in isolation.
Next, in Post 7, we’ll cover how to ship charts with confidence: Lint, Tests, Diff, OCI & Provenance.
Helm is powerful when it’s predictable.
Do it right once, and every future deployment becomes safer, faster, and auditable. Thanks for sticking around till the end, drop your comments and feedback below.

Top comments (0)