Helm Charts - Introduction to Advanced GitOps
14 Feb 2026- 1. The Helm Engine Room: Mastering
values.yaml,templates/, andcharts/ - 2. Demystifying
Chart.yaml: The Identity Card - 3. Advanced Context: Helm in a GitOps World
If you are new to Kubernetes, you have likely realized that managing dozens of static YAML files for every deployment, service, and ingress quickly becomes a nightmare. Enter Helm, the package manager for Kubernetes.
Helm allows you to bundle your Kubernetes resources into a single logical unit called a “Chart,” making your infrastructure reusable, shareable, and dynamic. In this guide, we will start with the absolute basics of Helm’s architecture and work our way up to how Staff Engineers deploy Helm in modern GitOps environments.
1. The Helm Engine Room: Mastering values.yaml, templates/, and charts/
To understand Helm and adhere to the DRY (Don’t Repeat Yourself) principle across your staging, UAT, and production environments, you need to understand the relationship between three critical components.
1. templates/: The Blueprint
If you look inside a standard Kubernetes environment, you see static YAML. In a Helm chart, the templates/ directory holds the structural blueprints for those YAML files, supercharged with Go template syntax.
Instead of hardcoding a namespace or a Docker image tag, you use placeholders enclosed in double curly braces ``. When you run helm install, the Helm engine evaluates the logic, replaces the placeholders, and outputs the final Kubernetes manifests.
**Example: templates/service.yaml**
apiVersion: v1
kind: Service
metadata:
name:
spec:
type:
ports:
- port:
targetPort: http
protocol: TCP
Notice how the structure remains pure Kubernetes YAML, but the data is completely dynamic.
2. values.yaml: The Control Panel
If templates/ is the blueprint, values.yaml is the control panel. It defines the default state of your application. When the Helm engine encounters `` in your template, it immediately looks inside values.yaml to resolve it.
**Example: values.yaml**
# Default configuration for sample-api
replicaCount: 3
service:
type: ClusterIP
port: 8080
💡 DevOps Best Practice: Value Overrides
The true power of values.yaml is that it acts as a base. You do not need to rewrite this file for different environments. Instead, you override specific values during deployment via the CLI:
helm upgrade --install prod-api ./sample-api \
--set service.type=LoadBalancer \
--set replicaCount=5
Or, even better, maintain separate environment files (like values-prod.yaml) and pass them in: helm install ... -f values-prod.yaml.
3. charts/: The Dependency Tree
Modern microservices rarely live in isolation. Your API might need a Redis cache or a PostgreSQL database. Instead of managing those separately, Helm allows you to bundle them using the charts/ directory (known as subcharts).
- Self-Contained: Subcharts live inside your main chart’s
charts/directory, making your deployment entirely self-contained. - Global Values: You can configure a subchart from your parent chart’s
values.yaml. For instance, set the PostgreSQL password globally, and Helm passes it down to the Postgres subchart.
2. Demystifying Chart.yaml: The Identity Card
Before a single template is rendered, Helm looks at the foundational blueprint of your package: the Chart.yaml. This file dictates API compatibility, manages dependencies, and governs your deployment lifecycle through strict versioning.
The Anatomy of a Chart.yaml
A production-ready Chart.yaml will look something like this:
apiVersion: v2
name: sample-api
description: A Helm chart for the internal Sample Web API
type: application
version: 1.2.0
appVersion: "2.1.4"
maintainers:
- name: platform-team
email: devops@yourcompany.com
dependencies:
- name: redis
version: 17.x.x
repository: https://charts.bitnami.com/bitnami
Key Fields Explained
| Field | Requirement | Description |
|---|---|---|
apiVersion |
Required | For Helm 3, this must be set to v2. |
name |
Required | The name of the chart. Must match the directory name. |
version |
Required | The version of the chart code itself (SemVer 2). Increment this when changing templates or values.yaml. |
appVersion |
Optional | The version of the software you are deploying (e.g., Docker image tag "2.1.4"). Wrap in quotes to avoid YAML parsing errors. |
dependencies |
Optional | A list of subcharts this chart relies on. |
💡 DevOps Best Practice: version vs appVersion
Getting this wrong can wreak havoc on CI/CD pipelines. Strictly decouple your chart versions from your application versions.
- Updating Application Code: If you update the NGINX image tag from
v1tov2, update theappVersionto"2.0"AND bump the chartversion(e.g.,1.0.0to1.0.1) because the default config changed. - Updating Infrastructure: If you add a new
HorizontalPodAutoscalertemplate but leave the app code alone, leaveappVersionas-is, but bump the chartversion(e.g.,1.0.1to1.1.0).
3. Advanced Context: Helm in a GitOps World
Now that you understand the mechanics of Helm, we must discuss how it is actually used in production today. In 2026, firing helm upgrade --install from your laptop or a Jenkins script is considered an anti-pattern. It leads to configuration drift and split-brain states where Git no longer reflects cluster reality.
Today, GitOps (powered by tools like ArgoCD and Flux) is the industry standard. This requires a mental shift: In GitOps, Helm is no longer your deployment orchestrator; it is strictly a packaging and templating format.
How GitOps Actually Handles Helm
- The ArgoCD Paradigm (Helm as a Templater): ArgoCD does not create a Helm release. It executes a
helm templatecommand behind the scenes, takes the raw YAML output, and uses its own sync engine to apply it. Standard CLI commands likehelm lsorhelm rollbackwill not work. - The Flux Paradigm (Helm as a Native Controller): Flux utilizes a
HelmReleaseCustom Resource. It actually pulls the chart, merges your Git-storedvalues.yaml, and natively installs it, giving you standard Helm CLI compatibility.
The Modern GitOps Flow
To visualize how Helm fits into continuous delivery, look at how we separate application code from infrastructure configuration:
Architectural Takeaway: The CI pipeline does not touch the cluster. Its only job is to build an immutable artifact and update the
values.yamlin the GitOps repository.
Implementation: Architectural Best Practices
- Embrace the Twin-Repo Strategy: Never store your Helm
values.yamlfiles in the same repository as your application source code. Keep app code (Repo A) separate from declarative infrastructure state (Repo B). - Standardize on OCI Artifacts: Package your Helm charts and push them to OCI-compliant registries (like AWS ECR or GitHub Container Registry) right alongside your Docker images. Both ArgoCD and Flux natively support pulling charts from OCI.
- Stop Putting Secrets in
values.yaml: Never commit base64-encoded secrets into Git. Instead, your Helm chart should template outExternalSecretresources to securely fetch credentials from AWS Secrets Manager or HashiCorp Vault at runtime.
Conclusion
By stripping Helm of its manual deployment responsibilities and using it strictly as a templating standard, we eliminate the flakiness of imperative rollouts. Let Helm build the puzzle pieces, and let GitOps put them together.