4.2 KiB
Helm Chart Reference & Advanced Patterns
This document contains detailed explanations and advanced patterns for Helm chart development.
🏠 Resource Ownership
Every Kubernetes resource must have exactly one owner. Never duplicate.
- If a chart creates a resource (e.g. Traefik creates IngressClass), don't create it elsewhere
- Before adding a template for a resource, verify no other chart/subchart already manages it
❌ Wrong: subchart A creates IngressClass AND subchart B creates IngressClass
✅ Right: one chart owns IngressClass, others do not touch it
🧹 values.yaml — Keep It Short and Identical Across Installations
values.yaml serves two purposes:
- Static baseline — identical for every installation.
- Umbrella glue — wires subcharts into a coherent whole. Shared values like
global.domainare defined once here.
Installation-specific values go exclusively in values.<instance>.yaml in the IaC/deployment repo. If a value varies between installations, it does not belong in the chart's values.yaml.
Forbidden in values.yaml:
- Empty string placeholders (
key: "") - Defaults for values that are hardcoded in templates
- Installation-specific data of any kind (hostnames, credentials)
| File | Location | Content |
|---|---|---|
values.yaml |
chart repo | minimal static baseline |
values.<instance>.yaml |
IaC repo | everything that varies per installation |
✅ required vs defaults
When a template needs a value:
1. Sensible default — hardcode it directly in the template. Do not add it to values.yaml.
2. Must come from deployer — use required. Do not put any entry in values.yaml.
| default in templates is forbidden.
# ✅ Sensible default
replicas: 1
# ✅ Must vary per installation
host: {{ required "global.domain is required" .Values.global.domain }}
# ❌ Forbidden — default in values.yaml adds noise
replicas: {{ .Values.replicas }} # values.yaml: replicas: 1
# ❌ Forbidden — hides missing config
host: {{ .Values.global.domain | default "" }}
📄 Configuration Files over values.yaml
Prefer files/ + ConfigMap over defining configuration in values.yaml.
- Put the config file in
files/ - Create a ConfigMap that reads it with
.AsConfig+tpl - Use
{{ .Values.global.xxx }}expressions inside the config file
# templates/configmap.yaml
data: {{ (tpl (.Files.Glob "files/*").AsConfig . ) | nindent 2 }}
Always use .AsConfig when the file contains template expressions or special characters.
🎂 Umbrella Chart Pattern
The standard structure is an umbrella chart that integrates multiple component charts.
my-umbrella/
├── Chart.yaml ← declares component charts as dependencies
├── values.yaml ← static baseline: shared across all installations
├── templates/ ← only cross-cutting resources that no subchart owns
└── charts/ ← built by helm dependency update
🔗 Glue Configuration
When a subchart cannot use Helm templates in its values (e.g. it reads values.<key> directly without templating), the umbrella values.yaml must coordinate the literal names.
Example: Tempo reading MinIO host:
minio:
fullnameOverride: "minio" # Forces service name to literal "minio"
tempo:
storage:
s3:
endpoint: "http://minio:9000" # Hardcoded literal match
⚡ KISS — Start Minimal
Do not add knobs, flags, or options that aren’t needed right now.
- Start with hardcoded values in the template.
- When parameterization is requested, expose it as
requiredtovalues.<instance>.yaml, notvalues.yaml.
🧪 Testing Templates
Always test with at least two different release names and namespaces to verify independence.
helm template app1 . --namespace default
helm template app2 . --namespace production
🐛 Interactive Infrastructure Debugging
When debugging live systems, act as an active operator:
- ✅ Request network access: Ask for
kubectl port-forwardif needed. - ✅ Use read-only terminal tools: Use
curlto test endpoints, orkubectl get/describe/logsdirectly in the terminal. - ❌ Never modify via terminal: Do not use the terminal to edit files or apply changes.