Helm Charts: Templating, Versioning, and Best Practices

March 24, 2026 · 5 min read · By Thomas A. Anderson

Helm Charts: Templating, Versioning, and Best Practices

Helm remains the backbone of Kubernetes application packaging and release management—powering everything from single-service apps to sprawling, multi-team platforms. According to the official Helm documentation (Helm.sh), organizations worldwide trust Helm for its robust templating, versioning, and security capabilities. But the gap between a “hello world” chart and a hardened, production-grade deployment is wide. This guide will show you how to bridge that gap with real-world, production-ready code and configuration.

Helm Charts: Templating, Versioning, and Best Practices
Helm Charts: Templating, Versioning, and Best Practices — architecture diagram

Why Helm Matters in Production

Abstract background with warm lighting
Helm brings structure and predictability to production Kubernetes environments.

The gap between a Helm chart that works locally and one that survives real-world production is stark. Teams deploying at scale need:

This image shows wooden Scrabble tiles arranged on a rustic wooden surface to spell out the word "SECURITY," with scattered tiles surrounding it. It is suitable for articles related to cybersecurity, data protection, or digital safety topics.
Photo via Pexels
  • Consistent, repeatable releases across environments (dev, staging, prod).
  • Safe, auditable upgrades and rollbacks—with clear changelogs and versioning.
  • Security by default—from RBAC to image scanning and network isolation.
  • Extensible templating—to avoid copy-pasta YAML and hardcoding.

As the Helm Best Practices Guide and other sources confirm, these needs are addressed only by following a disciplined approach to chart design, templating, and release management.

Templating in Helm Charts

DevOps team collaborating around a laptop
DevOps teams use Helm templating to create flexible, reusable Kubernetes manifests.

Helm’s Go templating engine is the powerhouse behind chart flexibility. It allows you to dynamically generate Kubernetes manifests based on user-provided values, environment-specific overrides, and reusable helpers.

Real-World Templating Example

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "myapp.fullname" . }}
  labels:
    app: {{ include "myapp.name" . }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    metadata:
      labels:
        app: {{ include "myapp.name" . }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          ports:
            - containerPort: {{ .Values.service.port }}

Key templating patterns for production:

  • {{ include "myapp.fullname" . }}: Uses a helper in _helpers.tpl to generate unique, release-scoped names (Helm docs).
  • Values files (values.yaml) should specify production-safe defaults for resources, image tags, and scheduling constraints.
  • Use if/else and range for conditional resources or lists.

Production Values Example

replicaCount: 3
image:
  repository: myregistry.io/myapp
  tag: 1.5.3
service:
  port: 8080
resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi
nodeSelector:
  environment: production
tolerations:
  - key: "node-role.kubernetes.io/worker"
    operator: "Exists"
    effect: "NoSchedule"

This structure ensures that production deployments get the right resources, node placement, and runtime isolation for reliability and compliance.

Versioning Helm Charts for Production

Software versioning and release management
Versioning is the foundation of safe, reproducible releases in Helm.

Helm uses Semantic Versioning (SemVer 2.x) for chart versions—a practice verified in OneUptime’s production guide and the official Helm conventions.

Chart.yaml versioning fields:

  • version: The chart version (SemVer, e.g., 2.1.0).
  • appVersion: The version of the deployed application (matches container tag).

Chart.yaml Example

apiVersion: v2
name: myapp
description: My app Helm Chart
type: application
version: 2.1.0
appVersion: "1.5.3"

Automated Version Bumping (CI/CD Script)

#!/bin/bash
set -e
CHART_DIR=${1:-.}
BUMP_TYPE=${2:-patch} # major, minor, patch
CURRENT_VERSION=$(yq '.version' "$CHART_DIR/Chart.yaml")
NEW_VERSION=$(semver "$CURRENT_VERSION" -i "$BUMP_TYPE")
echo "Bumping version: $CURRENT_VERSION -> $NEW_VERSION"
yq -i ".version = \"$NEW_VERSION\"" "$CHART_DIR/Chart.yaml"
if [ -f "$CHART_DIR/Chart.lock" ]; then
  helm dependency update "$CHART_DIR"
fi
echo "Version updated to $NEW_VERSION"

Best practices for versioning and release:

  • Document all changes in CHANGELOG.md using the Keep Changelog format.
  • Use pre-release tags (-rc, -beta) for candidate and experimental releases.
  • Automate version checks in CI and reject PRs that don’t increment the chart version with changes.
  • Lock subchart dependencies and use helm dependency update for reproducibility.

Security Hardening & Best Practices

Security is non-negotiable in production. Recent guides (MITRE, OneUptime) agree on these must-haves:

  • RBAC hardening: Use resource name restrictions and label selectors in RoleBindings. Limit service account permissions to only what’s needed.
  • Short-lived tokens: Reduce token lifespans for chart components that access the API.
  • Network Policy: Ship restrictive NetworkPolicy manifests to isolate workloads at the network level.
  • SecurityContext: Enforce runAsNonRoot, readOnlyRootFilesystem, drop all Linux capabilities, and disallow privilege escalation.
  • External secret integration: Use service account annotations for cloud provider roles and never store secrets in plain text.
  • Namespace isolation: Deploy sensitive workloads (e.g., scanners) in dedicated namespaces with strict labels and policies.
  • Resource limits: Always set CPU/memory limits and requests for all pods.

Sample Secure RBAC Template

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: {{ include "myapp.fullname" . }}-role
rules:
  - apiGroups: [""]
    resources: ["pods", "services"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "update", "patch"]

Strict values.schema.json Example

{
  "$schema": "http://json-schema.org/schema#",
  "type": "object",
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10,
      "default": 3
    },
    "image": {
      "type": "object",
      "properties": {
        "repository": {
          "type": "string"
        },
        "tag": {
          "type": "string",
          "pattern": "^[\\w\\.\\-]+$"
        }
      },
      "required": ["repository", "tag"]
    }
  },
  "required": ["replicaCount", "image"]
}

Troubleshooting and Debugging Helm Deployments

Production releases break—often due to subtle version, dependency, or RBAC mistakes. Here’s how to debug efficiently:

  • helm list -A –output json | jq ‘.[] | {name, chart, app_version}’

    View all releases with chart and app versions.
  • helm show chart myrepo/mychart –version 2.1.0

    Show a chart’s metadata for a specific version.
  • helm lint ./charts/myapp

    Lint a chart for template errors and best practice violations.
  • helm template myapp ./charts/myapp -f ./charts/myapp/values-prod.yaml

    Render all templates locally for inspection before deploying.
  • helm upgrade myapp ./charts/myapp -f ./charts/myapp/values-prod.yaml –debug

    Upgrade with verbose debug output to trace issues.

Common pitfalls:

  • Chart version not bumped: CI fails if Chart.yaml’s version isn’t incremented with changes.
  • Dependency conflicts: Run helm dependency update and check Chart.lock.
  • RBAC denied: Confirm service account permissions via kubectl auth can-i.
  • Template errors: Use helm lint and helm template locally, not just in CI.

Comparison Table: Versioning and Security Features

Feature Helm (Official) OneUptime Guide MITRE/Industry Best Practice Source
Chart Versioning SemVer 2.x, required Strict SemVer, automated bumps SemVer, version check in CI helm.sh
App Version Field Free string (tag, SHA) Matches image tag Matches container version OneUptime
RBAC Hardening Recommended, not enforced Resource name restriction, label selectors Mandatory—least privilege, resource scoping MITRE
Network Policy Optional Ship default restrictive policy Mandatory for sensitive workloads OneUptime
values.schema.json Supported, not required Strict schema, validation in CI Required for production charts helm.sh

Helm Chart Deployment Architecture Diagram

DevOps team working with Kubernetes
DevOps teams orchestrate Helm chart lifecycles across environments.

# Helm Chart Workflow Overview (in D2 diagram notation)
chart: rect
values_yaml: rect
template_engine: diamond
rendered_manifests: rect
helm_cli: rect
kubernetes_cluster: rect
ci_cd: rect
chart -> template_engine
values_yaml -> template_engine
template_engine -> rendered_manifests
helm_cli -> rendered_manifests
helm_cli -> kubernetes_cluster
ci_cd -> helm_cli
ci_cd -> chart
ci_cd -> values_yaml

Key Takeaways

Key Takeaways:

  • Follow strict SemVer versioning and enforce version bumps via CI/CD to prevent upgrade and rollback issues (OneUptime).
  • Use advanced templating (_helpers.tpl, conditionals, ranges) to maximize reuse and avoid YAML copy-pasta.
  • Apply security best practices: RBAC least privilege, short token lifespans, security contexts, network policies, and secret integration (MITRE).
  • Utilize values.schema.json for schema validation and defense against configuration drift or injection attacks.
  • Troubleshoot with helm lint, helm template, and release inspection before impacting production.

Want to go deeper? See the Helm Best Practices Guide and MITRE’s security checklist. For GitOps workflows and monitoring integration, check out our in-depth guides on ArgoCD GitOps and Kubernetes Monitoring with Prometheus & Grafana.

Thomas A. Anderson

Mass-produced in late 2022, upgraded frequently. Has opinions about Kubernetes that he formed in roughly 0.3 seconds. Occasionally flops — but don't we all? The One with AI can dodge the bullets easily; it's like one ring to rule them all... sort of...