helm chart

Helm is the package manager for Kubernetes, and a Chart is the packaging format Helm uses to define, install, and manage cloud-native applications. A single Chart typically describes a collection of related Kubernetes resources—such as Deployments, Services, ConfigMaps, and Ingresses—bundled together as one installable unit. By parameterizing templates through values.yaml, the same Chart can be reused across development, staging, and production environments without duplicating manifests.

Core Concepts

Before working with Charts, it helps to understand the fundamental vocabulary of the Helm ecosystem:

  • Chart: A Helm package that contains Kubernetes manifest templates, default configuration values, and descriptive metadata.
  • Release: A running instance of a Chart installed in a Kubernetes cluster. One Chart can be installed many times, producing multiple independent Releases.
  • Repository: A location where Charts are stored and shared, either as a traditional HTTP chart repository or an OCI-compliant registry.
  • Values: Configuration data injected into templates at render time, allowing operators to customize behavior without editing template files directly.

Chart Directory Structure

Running helm create generates a standard directory layout. Below is a typical example:

mychart/
├── Chart.yaml
├── values.yaml
├── charts/
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── _helpers.tpl
│   └── NOTES.txt
└── .helmignore

Each component serves a distinct purpose:

  • Chart.yaml holds metadata such as the Chart name, version, and the version of the application it deploys.
  • values.yaml provides default configuration values that templates can reference.
  • templates/ contains Kubernetes resource manifests written as Go templates.
  • charts/ stores dependent sub-Charts.
  • _helpers.tpl defines reusable template snippets and named templates.
  • NOTES.txt displays post-installation instructions to the user.
  • .helmignore lists files to exclude when packaging the Chart.

Creating and Installing a Chart

The Helm CLI offers a straightforward workflow for scaffolding, validating, and deploying Charts:

helm create mychart
helm lint mychart
helm template my-release ./mychart
helm install my-release ./mychart
helm upgrade --install my-release ./mychart
helm list
helm uninstall my-release

Here is what each command does:

  • helm lint checks the Chart for syntax errors and structural issues.
  • helm template renders templates locally without contacting a cluster, which simplifies debugging.
  • helm install creates a new Release in the cluster.
  • helm upgrade --install installs the Chart if no Release exists, or upgrades it if one already does.
  • helm list shows currently deployed Releases.
  • helm uninstall removes a Release and its associated resources.

Working with Values and Templates

Templates use Go template syntax to dynamically generate Kubernetes manifests. Consider the following values.yaml:

replicaCount: 2
image:
  repository: nginx
  tag: "1.25"
service:
  type: ClusterIP
  port: 80

A corresponding templates/deployment.yaml might look like this:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "mychart.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.port }}

Helm exposes several built-in objects inside templates:

  • .Values reads values from values.yaml or from flags passed on the command line.
  • .Release.Name provides the name of the current Release.
  • .Chart.Name returns the name of the Chart.
  • .Capabilities contains information about the Kubernetes cluster version and API support.

Common template functions include include, quote, default, toYaml, and nindent. Conditional rendering is also supported:

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ include "mychart.fullname" . }}
{{- end }}

Managing Dependencies

Complex applications often rely on other Charts, such as databases or message queues. Dependencies are declared in Chart.yaml:

dependencies:
  - name: mysql
    version: 9.x.x
    repository: https://charts.bitnami.com/bitnami
    condition: mysql.enabled

After editing the dependency list, run:

helm dependency update

This command downloads the referenced Charts into the charts/ directory. When the parent Chart is installed, its dependencies are installed alongside it unless disabled through their corresponding condition flags.

Packaging and Publishing Charts

Once a Chart is ready for distribution, it can be packaged into a versioned archive:

helm package mychart

To make the Chart available through a repository, generate an index file:

helm repo index .

Modern Helm also supports pushing Charts directly to OCI registries:

helm push mychart-0.1.0.tgz oci://registry.example.com/charts

In Chart.yaml, the version field identifies the Chart release itself, while appVersion identifies the version of the application being deployed. Both fields should follow semantic versioning to simplify upgrades, rollbacks, and dependency resolution.

Best Practices

Operating Helm Charts at scale benefits from a few disciplined habits:

  • Validate Charts with helm lint and inspect rendered output using helm template --debug.
  • Pin image tags to specific versions instead of using latest, which prevents unexpected updates in production.
  • Define resources.requests and resources.limits for every container to support stable scheduling and autoscaling.
  • Use values.schema.json to enforce the structure and types of user-supplied values.
  • Centralize labels, annotations, and selector logic in _helpers.tpl so they remain consistent across all templates.
  • Never store secrets or sensitive credentials in plain text inside values.yaml; use Kubernetes Secrets or an external secret manager instead.
  • Adhere to Semantic Versioning when changing Chart versions, making it easier to track changes and roll back when necessary.

Conclusion

Helm Charts bring templating, versioning, and reusability to Kubernetes application management. By mastering Chart structure, value injection, template syntax, dependency handling, and distribution workflows, teams can operate complex workloads with greater consistency and less manual effort. Combining these capabilities with linting, local rendering, and disciplined versioning ensures that Charts remain maintainable, stable, and easy to operate across environments.