Deploy Applications to Kubernetes with Helm Charts
Package, version, and deploy Kubernetes applications using Helm charts with value overrides, template functions, and release management for reproducible infrastructure
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Deploy Applications to Kubernetes with Helm Charts
Package and deploy applications to Kubernetes using Helm, the package manager for K8s. This recipe covers chart structure, templating with values, release upgrades and rollbacks, and dependency management for production-grade deployments.
When to Use This
- You deploy the same application to multiple environments with different configurations
- Kubernetes manifests become repetitive and hard to maintain across teams
- You need versioned releases with easy rollback capabilities
Solution
1. Chart Structure
myapp/
Chart.yaml # Chart metadata
values.yaml # Default configuration values
values.prod.yaml # Production overrides
templates/
_helpers.tpl # Named template helpers
deployment.yaml
service.yaml
ingress.yaml
configmap.yaml
charts/ # Subchart dependencies
2. Chart Metadata
# Chart.yaml
apiVersion: v2
name: myapp
description: A Helm chart for MyApp
type: application
version: 1.2.0
appVersion: "2.5.1"
dependencies:
- name: postgresql
version: 12.x.x
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
3. Template with Values
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "myapp.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
ports:
- containerPort: {{ .Values.service.port }}
env:
- name: DATABASE_URL
value: {{ .Values.database.url | quote }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
4. Default and Override Values
# values.yaml
replicaCount: 2
image:
repository: myregistry/myapp
tag: ""
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 3000
ingress:
enabled: false
database:
url: "postgres://localhost:5432/myapp"
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
# values.prod.yaml
replicaCount: 5
ingress:
enabled: true
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
resources:
limits:
cpu: 2000m
memory: 2Gi
5. Install and Upgrade
# Install with default values
helm install myapp ./myapp
# Install with production overrides
helm install myapp ./myapp -f values.prod.yaml
# Upgrade existing release
helm upgrade myapp ./myapp -f values.prod.yaml
# Rollback to previous revision
helm rollback myapp 2
# List release history
helm history myapp
6. Named Template Helpers
# templates/_helpers.tpl
{{/* Expand the name of the chart */}}
{{- define "myapp.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/* Create a default fully qualified app name */}}
{{- define "myapp.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
How It Works
- Charts package Kubernetes manifests into versioned, configurable units
- Templates use Go templating to inject values into YAML resources
- Values files provide environment-specific configuration overrides
- Releases track deployed chart versions for easy rollback
Production Considerations
- Store charts in a registry (Harbor, OCI registry) rather than local files
- Use
helm lintandhelm templateto validate before deploying - Pin dependency versions explicitly to prevent unexpected upgrades
Common Mistakes
- Hardcoding environment values in templates instead of values files
- Forgetting to update
Chart.versionwhen making changes - Not testing
helm upgradebefore applying to production
FAQ
Q: How is this different from Kustomize? A: Helm uses templating and packaging for reusable charts. Kustomize uses overlays and patches without templating. Helm is better for distributing complex applications; Kustomize is simpler for internal overlays.
Q: Can I use Helm with CI/CD?
A: Yes. Use helm upgrade --install for idempotent deployments in pipelines. Combine with helm diff to preview changes before applying.