Lesson 02 Your First Chart
Theme:

Theory

Reading time: ~20 minutes  ·  Prerequisites: Lesson 01 (Introduction to Helm), basic Kubernetes familiarity

1. Scaffolding a Chart with helm create

Every Helm chart starts as a directory containing a specific set of files. While you could create these files by hand, the helm create command generates a fully functional, best-practice chart in seconds. This scaffolded chart deploys an nginx web server with a Deployment, Service, and optional Ingress — a working application you can immediately install and then customize.

helm create myapp

This single command produces the following directory tree:

myapp/
  Chart.yaml
  values.yaml
  charts/
  templates/
    deployment.yaml
    service.yaml
    ingress.yaml
    hpa.yaml
    serviceaccount.yaml
    _helpers.tpl
    NOTES.txt
    tests/
      test-connection.yaml
  .helmignore

Each file serves a distinct purpose. The rest of this reading walks through every component in detail, explaining not just what each file contains but why it exists and how the pieces connect during rendering and deployment.

Tip: Always Start with helm create

Even experienced chart authors use helm create as a starting point. The generated templates include proper labels, selectors, helper functions, and conditional blocks that are easy to forget when writing from scratch. Delete what you do not need rather than building from zero.

2. Chart.yaml — The Chart’s Identity

The Chart.yaml file is the only strictly required file in a Helm chart (besides at least one template or dependency). It declares the chart’s metadata: its name, version, and description. Helm reads this file to identify the chart, resolve dependencies, and display information in search results.

apiVersion: v2
name: myapp
description: A Helm chart for deploying my application
type: application
version: 0.1.0
appVersion: "1.0.0"

Required Fields

Optional but Important Fields

version vs. appVersion

These two versions are independent. You might release chart version 0.3.0 that still deploys app version 1.0.0 because you only changed the Ingress template. Conversely, bumping the app to 2.0.0 without touching templates still warrants a chart version bump (e.g., 0.3.1) so consumers know something changed.

3. values.yaml — Configuration Defaults

The values.yaml file contains the default configuration for the chart. Every key defined here becomes accessible in templates via the .Values object. Users override these defaults at install time using --set flags or -f value files, without modifying the chart itself.

replicaCount: 1

image:
  repository: nginx
  pullPolicy: IfNotPresent
  tag: ""

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  className: ""
  hosts:
    - host: chart-example.local
      paths:
        - path: /
          pathType: ImplementationSpecific

resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

How Values Flow into Templates

Templates reference values using Go template syntax. For example, {{ .Values.replicaCount }} resolves to 1 unless overridden. Nested values use dot notation: {{ .Values.image.repository }} yields nginx. This separation of configuration from templates is what makes Helm charts reusable — the same chart can deploy to development, staging, and production with different value files.

# Override a single value
helm install myrelease ./myapp --set replicaCount=3

# Override with a custom values file
helm install myrelease ./myapp -f production-values.yaml

# Combine both (--set takes precedence)
helm install myrelease ./myapp -f production-values.yaml --set image.tag=v2.1.0

Value Precedence

When multiple sources provide the same key, Helm merges them with a clear priority order: --set flags override -f file values, which override the chart’s built-in values.yaml. Within multiple -f files, the last file wins.

4. The templates/ Directory

The templates/ directory holds Go-templated Kubernetes manifests. When Helm renders a chart, it processes every .yaml and .tpl file in this directory (recursively), substituting values and evaluating control structures to produce valid Kubernetes YAML.

Core Template Files

_helpers.tpl — Reusable Template Partials

Files prefixed with an underscore are not rendered as Kubernetes manifests. Instead, _helpers.tpl defines named templates (partials) that other templates include. The scaffolded helpers provide:

# Using a helper in deployment.yaml
metadata:
  name: {{ include "myapp.fullname" . }}
  labels:
    {{- include "myapp.labels" . | nindent 4 }}

NOTES.txt — Post-Install Instructions

The NOTES.txt file is a special template that does not produce a Kubernetes resource. Instead, its rendered output is displayed to the user after helm install and helm upgrade. Use it to provide connection instructions, port-forward commands, or next steps.

1. Get the application URL by running:
{{- if .Values.ingress.enabled }}
  http://{{ (index .Values.ingress.hosts 0).host }}
{{- else if eq .Values.service.type "ClusterIP" }}
  kubectl port-forward svc/{{ include "myapp.fullname" . }} 8080:{{ .Values.service.port }}
  Visit http://127.0.0.1:8080
{{- end }}

Because NOTES.txt is a template, it can adapt its output based on the user’s configuration — showing different instructions depending on whether Ingress is enabled, the service type, or the namespace.

5. Supporting Files

charts/ Directory

The charts/ directory holds dependency charts (subcharts). When you declare dependencies in Chart.yaml and run helm dependency update, Helm downloads the dependency archives into this directory. For a simple chart with no dependencies, this directory is empty.

.helmignore

Similar to .gitignore, the .helmignore file lists patterns for files that should be excluded when packaging the chart with helm package. The default includes common entries like .git/, *.swp, and .DS_Store.

6. The Release Lifecycle

Helm manages the complete lifecycle of a release — the running instance of a chart in a cluster — through four primary commands.

helm install

Creates a new release. Helm renders all templates with the provided values, submits the resulting manifests to the Kubernetes API, and stores the release metadata (including the rendered manifests and values) as a Secret in the target namespace.

helm install myrelease ./myapp --namespace production --create-namespace

helm upgrade

Updates an existing release. Helm re-renders the templates with new values or a new chart version, computes a diff against the current state, and applies only the changes. Each upgrade increments the revision number.

helm upgrade myrelease ./myapp --set image.tag=v2.0.0

helm rollback

Reverts a release to a previous revision. Importantly, rollback creates a new revision rather than deleting history. Rolling back from revision 3 to revision 1 produces revision 4 with the configuration from revision 1.

helm rollback myrelease 1

helm uninstall

Removes all Kubernetes resources associated with the release and deletes the release metadata. After uninstall, the release name becomes available for reuse.

helm uninstall myrelease --namespace production

7. Inspecting Releases

Beyond the lifecycle commands, Helm provides inspection commands that are essential for debugging and auditing.

CommandPurpose
helm listList all releases in the current namespace (add -A for all namespaces)
helm status <release>Show the current state, revision, namespace, and NOTES output
helm history <release>Display all revisions with timestamps, status, and descriptions
helm get manifest <release>Print the rendered Kubernetes manifests for the current revision
helm get values <release>Show the user-supplied values (add -a for all computed values)
helm get notes <release>Re-display the NOTES.txt output

These commands query the release Secrets stored in the cluster, not the local chart directory. This means you can inspect a release even if you no longer have the chart source on your machine.

Tip: Debug Before Installing

Use helm template ./myapp to render templates locally without contacting the cluster, or helm install --dry-run --debug to simulate an install and see the rendered output. Both are invaluable for catching template errors before they reach your cluster.

8. Putting It All Together

A typical workflow for your first chart looks like this:

  1. Scaffold — Run helm create myapp to generate the chart structure.
  2. Customize — Edit values.yaml to set your image, ports, and resource limits. Modify templates if needed.
  3. Validate — Run helm template ./myapp to verify the rendered output looks correct.
  4. Install — Deploy with helm install myrelease ./myapp and check the NOTES output.
  5. Iterate — Make changes and run helm upgrade. If something breaks, helm rollback.
  6. Inspect — Use helm status, helm history, and helm get to understand the release state.
  7. Clean up — When done, helm uninstall removes everything.

This cycle — create, install, upgrade, rollback, inspect, uninstall — is the foundation of all Helm operations. Every advanced topic in later lessons (dependencies, hooks, testing, CI/CD integration) builds on this core lifecycle.