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
FlagPurpose
-p / --starterUse a starter chart as scaffold
--starter-dirOverride starters location

2. Understanding Chart Directory Layout

PathPurpose
Chart.yamlMetadata + dependencies
values.yamlDefault configuration
values.schema.jsonJSON Schema validation
charts/Vendored subcharts
crds/CRDs installed before templates (not templated)
templates/Rendered manifests
templates/_*.tplHelper files (not rendered)
templates/NOTES.txtPost-install notes
templates/tests/Test pods/jobs
.helmignoreGlob list of excluded files
README.md / LICENSEDocumentation (included in package)

3. Creating Chart.yaml Metadata

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

TypeBehavior
application (default)Installable chart that produces release
libraryReusable 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

PatternExcluded
.gitGit metadata
*.tgzPreviously packaged charts
.DS_StoremacOS metadata
OWNERSRepo ownership files
tests/integration/Heavy non-shipped tests
Note: Uses gitignore-like syntax; rooted at chart directory.

7. Organizing Template Files

ConventionExample
One resource per filedeployment.yaml, service.yaml
Helpers prefixed with __helpers.tpl, _labels.tpl
Group by featurerbac/, monitoring/ subdirs
Multi-doc for relatedSingle 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

ContentSource
Packaged subcharts (.tgz)Created by helm dependency update
Unpacked subchart dirsManually 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.
BehaviorDetail
Install orderAlways before templates/
UpgradeNot upgraded by Helm — manage manually or via separate chart
UninstallLeft 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 -}}