Topic 7.2
Writing Your Own Chart
In one line
helm create scaffolds a working starter chart — turning your own Deployment, Service, and ConfigMap from earlier phases into genuinely reusable, parameterized templates is most of the real skill.
Key ideas
- 01
helm create <name>scaffolds a complete, working starter chart with sensible example templates already in place — genuinely the standard way to begin a new chart, since starting from a proven structure is faster and less error-prone than building the directory layout from scratch. - 02
Converting an existing plain manifest (like Phase 1.1's Deployment) into a chart template means identifying which specific VALUES should actually be configurable — the image tag, the replica count, resource requests/limits — and replacing those specific fields with
{{ .Values.xxx }}references, while leaving everything that's genuinely fixed and identical across every environment as plain, literal YAML. - 03
{{ .Release.Name }}is a BUILT-IN template variable (not from your ownvalues.yaml) referring to the release name given at install time — genuinely useful for naming resources uniquely per release ({{ .Release.Name }}-deployment), letting the SAME chart be installed multiple times in the same cluster under different release names without any naming collision. - 04
Helm templates support genuine LOGIC, not just simple substitution —
{{ if .Values.ingress.enabled }}conditionally includes an entire block (or an entire separate template file) only if a value is set a certain way, and{{ range .Values.items }}loops over a list, generating repeated YAML for each entry — genuinely powerful once a chart needs to support meaningfully different configurations, not just different values for the same fixed structure. - 05
helm lint <chart>checks a chart for common mistakes and best-practice violations BEFORE you ever try installing it — genuinely worth running routinely while developing a chart, catching issues like missing required fields or malformed templates early, the same 'catch it before it costs you' philosophy CI (Git's own course, Phase 7.3) is built around. - 06
A well-written chart should have SENSIBLE DEFAULTS in
values.yaml— a chart that requires every single value to be explicitly overridden just to install successfully at all defeats much of the point; the goal is a chart that installs and runs correctly with ZERO overrides for a reasonable default case, while still allowing genuine customization when actually needed.
Code & diagrams
The same Deployment from Phase 1.1, now genuinely parameterized with logic and built-in variables.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-my-app
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}-my-app
template:
metadata:
labels:
app: {{ .Release.Name }}-my-app
spec:
containers:
- name: my-app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources:
requests:
cpu: {{ .Values.resources.requests.cpu }}
memory: {{ .Values.resources.requests.memory }}
{{- if .Values.healthCheck.enabled }}
readinessProbe:
httpGet:
path: {{ .Values.healthCheck.path }}
port: {{ .Values.service.port }}
{{- end }}Sensible defaults — this chart installs correctly with ZERO overrides.
replicaCount: 2
image:
repository: my-registry/my-app
tag: "1.5.0"
resources:
requests:
cpu: "250m"
memory: "256Mi"
service:
port: 8080
healthCheck:
enabled: true
path: /healthScaffold, lint, and install your own chart — starting from a proven baseline structure.
# Scaffold a new starter chart
helm create my-app-chart
# Check it for common mistakes before ever installing it
helm lint ./my-app-chart
# Preview it, confirming logic (like the conditional readinessProbe) renders correctly
helm template test-release ./my-app-chart
# Try it with healthCheck disabled — confirm the readinessProbe block disappears entirely
helm template test-release ./my-app-chart --set healthCheck.enabled=false
# Install two SEPARATE releases of the SAME chart, in the same cluster —
# {{ .Release.Name }} prevents any naming collision between them
helm install release-a ./my-app-chart
helm install release-b ./my-app-chart
kubectl get deployments
# release-a-my-app and release-b-my-app — both exist independentlyExplain it without notes
Why does {{ .Release.Name }} matter for letting the same chart be installed multiple times in one cluster without conflict?
Why is having sensible defaults in values.yaml considered a genuinely important part of writing a good chart, not just a nice-to-have?
Practice
Scaffold a new chart with helm create, then modify its default Deployment template to use at least one conditional block ({{ if }}) controlling whether a specific field appears at all.
Run helm lint against your own chart and fix any warnings it surfaces before considering the chart genuinely done.
Trade-offs
- ↔
Writing a genuinely well-parameterized, reusable chart takes real, deliberate upfront thought about WHICH values should be configurable (and with what sensible defaults) — over-parameterizing everything imaginable produces a chart that's genuinely harder to understand and use than one that's parameterized specifically for the handful of things that actually vary in practice; a chart trying to be infinitely flexible often ends up being flexible in ways nobody actually needs, at a real cost to clarity.
Done when you can
I can convert a plain Kubernetes manifest into a genuinely parameterized Helm template.
I understand built-in variables like {{ .Release.Name }} and can use conditional/loop logic in a template.
I know to run helm lint routinely while developing a chart, and to provide sensible defaults in values.yaml.