Creating Chart Structure
1. Generating New Chart
Example: Scaffold a chart
helm create myapp
tree myapp
# myapp/
# ├── Chart.yaml
# ├── values.yaml
# ├── .helmignore
# ├── charts/
# └── templates/
# ├── _helpers.tpl
# ├── deployment.yaml
# ├── service.yaml
# ├── ingress.yaml
# ├── serviceaccount.yaml
# ├── hpa.yaml
# ├── NOTES.txt
# └── tests/test-connection.yaml
| Flag | Purpose |
-p / --starter | Use a starter chart as scaffold |
--starter-dir | Override starters location |
2. Understanding Chart Directory Layout
| Path | Purpose |
Chart.yaml | Metadata + dependencies |
values.yaml | Default configuration |
values.schema.json | JSON Schema validation |
charts/ | Vendored subcharts |
crds/ | CRDs installed before templates (not templated) |
templates/ | Rendered manifests |
templates/_*.tpl | Helper files (not rendered) |
templates/NOTES.txt | Post-install notes |
templates/tests/ | Test pods/jobs |
.helmignore | Glob list of excluded files |
README.md / LICENSE | Documentation (included in package) |
Example: Minimal Chart.yaml
apiVersion: v2
name: myapp
description: REST API for orders
type: application
version: 1.4.2 # chart version (SemVer)
appVersion: "2.7.0" # app version (string, quoted)
kubeVersion: ">=1.27.0"
icon: https://example.com/icon.png
4. Defining Chart Type
| Type | Behavior |
application (default) | Installable chart that produces release |
library | Reusable templates only; cannot be installed standalone |
5. Creating values.yaml File
Example: Idiomatic structure
replicaCount: 2
image:
repository: ghcr.io/acme/api
tag: "" # defaults to .Chart.AppVersion
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
resources:
limits: { cpu: 500m, memory: 512Mi }
requests: { cpu: 100m, memory: 128Mi }
ingress:
enabled: false
className: nginx
hosts: []
6. Creating .helmignore File
| Pattern | Excluded |
.git | Git metadata |
*.tgz | Previously packaged charts |
.DS_Store | macOS metadata |
OWNERS | Repo ownership files |
tests/integration/ | Heavy non-shipped tests |
Note: Uses gitignore-like syntax; rooted at chart directory.
7. Organizing Template Files
| Convention | Example |
| One resource per file | deployment.yaml, service.yaml |
Helpers prefixed with _ | _helpers.tpl, _labels.tpl |
| Group by feature | rbac/, monitoring/ subdirs |
| Multi-doc for related | Single file with --- between related resources |
8. Creating NOTES.txt File
Example: NOTES.txt with conditional URL
Thank you for installing {{ .Chart.Name }}.
{{- if .Values.ingress.enabled }}
URL: https://{{ (index .Values.ingress.hosts 0).host }}
{{- else }}
Port-forward:
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ include "myapp.fullname" . }} 8080:{{ .Values.service.port }}
{{- end }}
9. Setting Up charts/ Subdirectory
| Content | Source |
Packaged subcharts (.tgz) | Created by helm dependency update |
| Unpacked subchart dirs | Manually vendored for local development |
10. Creating crds/ Directory
Warning: Files in crds/ are NOT templated. They install once before any other resource. Helm will not upgrade or uninstall them automatically.
| Behavior | Detail |
| Install order | Always before templates/ |
| Upgrade | Not upgraded by Helm — manage manually or via separate chart |
| Uninstall | Left in cluster (data safety) |
| Skip flag | --skip-crds on install |
11. Creating templates/_helpers.tpl
Example: Standard helpers from helm create
{{/* Expand the name of the chart. */}}
{{- define "myapp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/* Fully qualified app name. */}}
{{- define "myapp.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{/* Common labels */}}
{{- define "myapp.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}