Skip to content

Helm Chart Guide

Package and deploy USL applications using Helm, the Kubernetes package manager.

Overview

Helm charts provide: - Templating: Reusable configurations across environments - Versioning: Track and rollback deployments - Dependencies: Manage related services - Values: Environment-specific configuration

Generate Helm Chart

usl compile app.usl --deployment helm --output ./helm-chart

Generated structure:

helm-chart/
├── Chart.yaml           # Chart metadata
├── values.yaml          # Default values
├── .helmignore         # Files to ignore
├── README.md           # Installation guide
└── templates/
    ├── deployment.yaml
    ├── service.yaml
    ├── ingress.yaml
    ├── configmap.yaml
    ├── hpa.yaml
    ├── servicemonitor.yaml
    ├── _helpers.tpl    # Template helpers
    └── NOTES.txt       # Post-install notes

Chart.yaml

Metadata about the chart:

apiVersion: v2
name: my-app
description: A Helm chart for my-app generated by USL Compiler
type: application
version: 0.1.0
appVersion: "1.0.0"
keywords:
  - usl
  - microservice
  - api
maintainers:
  - name: Your Team
    email: team@example.com

values.yaml

Default configuration values:

replicaCount: 3

image:
  repository: my-app
  pullPolicy: IfNotPresent
  tag: "latest"

service:
  type: ClusterIP
  port: 80
  targetPort: 8000

ingress:
  enabled: true
  className: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
  hosts:
    - host: my-app.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: my-app-tls
      hosts:
        - my-app.example.com

resources:
  limits:
    cpu: 2000m
    memory: 2Gi
  requests:
    cpu: 1000m
    memory: 512Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

monitoring:
  enabled: true
  serviceMonitor:
    enabled: true
    interval: 30s

Installation

Install from Local Chart

helm install my-release ./helm-chart

Install with Custom Values

helm install my-release ./helm-chart \
  --values production-values.yaml \
  --namespace production \
  --create-namespace

Install with CLI Overrides

helm install my-release ./helm-chart \
  --set replicaCount=5 \
  --set image.tag=v1.2.3 \
  --set ingress.hosts[0].host=api.example.com \
  --namespace production

Environment-Specific Values

development-values.yaml

replicaCount: 1

image:
  tag: "dev"
  pullPolicy: Always

ingress:
  enabled: true
  hosts:
    - host: my-app-dev.example.com
  tls: []

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 250m
    memory: 256Mi

autoscaling:
  enabled: false

production-values.yaml

replicaCount: 5

image:
  tag: "v1.2.3"
  pullPolicy: IfNotPresent

ingress:
  enabled: true
  annotations:
    nginx.ingress.kubernetes.io/rate-limit: "1000"
  hosts:
    - host: api.example.com
  tls:
    - secretName: api-tls
      hosts:
        - api.example.com

resources:
  limits:
    cpu: 4000m
    memory: 4Gi
  requests:
    cpu: 2000m
    memory: 2Gi

autoscaling:
  enabled: true
  minReplicas: 5
  maxReplicas: 20
  targetCPUUtilizationPercentage: 60

Common Operations

List Releases

helm list -n production

Upgrade Release

helm upgrade my-release ./helm-chart \
  --values production-values.yaml \
  --namespace production

Rollback Release

# List revision history
helm history my-release -n production

# Rollback to previous version
helm rollback my-release -n production

# Rollback to specific revision
helm rollback my-release 3 -n production

Uninstall Release

helm uninstall my-release -n production

Test Release

helm test my-release -n production

Template Testing

Dry Run

Preview generated manifests:

helm install my-release ./helm-chart \
  --dry-run \
  --debug \
  --namespace production

Template Rendering

Render templates locally:

helm template my-release ./helm-chart \
  --values production-values.yaml \
  --namespace production \
  > rendered.yaml

Lint Chart

Validate chart structure:

helm lint ./helm-chart

Template Functions

Helpers (_helpers.tpl)

Common template patterns:

{{/* Generate full name */}}
{{- define "chart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}

{{/* Common labels */}}
{{- define "chart.labels" -}}
helm.sh/chart: {{ include "chart.chart" . }}
{{ include "chart.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

Using in Templates

metadata:
  name: {{ include "chart.fullname" . }}
  labels:
    {{- include "chart.labels" . | nindent 4 }}

Advanced Features

Conditional Resources

{{- if .Values.monitoring.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: {{ include "chart.fullname" . }}
spec:
  selector:
    matchLabels:
      {{- include "chart.selectorLabels" . | nindent 6 }}
{{- end }}

Loops

{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
  http:
    paths:
    {{- range .paths }}
    - path: {{ .path }}
      pathType: {{ .pathType }}
      backend:
        service:
          name: {{ include "chart.fullname" $ }}
    {{- end }}
{{- end }}

Dependencies

Add to Chart.yaml:

dependencies:
  - name: postgresql
    version: "12.1.2"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
  - name: redis
    version: "17.3.7"
    repository: "https://charts.bitnami.com/bitnami"
    condition: redis.enabled

Install dependencies:

helm dependency update ./helm-chart

Hooks

Lifecycle hooks for database migrations, tests:

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "chart.fullname" . }}-migration
  annotations:
    "helm.sh/hook": pre-upgrade,pre-install
    "helm.sh/hook-weight": "-1"
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    spec:
      containers:
      - name: migration
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        command: ["python", "migrate.py"]
      restartPolicy: Never

Packaging & Distribution

Package Chart

helm package ./helm-chart
# Creates: my-app-0.1.0.tgz

Create Chart Repository

# Create index
helm repo index . --url https://charts.example.com

# Serve via HTTP
helm serve --address localhost:8879 --url http://localhost:8879/charts/

OCI Registry

Push to OCI-compatible registry:

helm package ./helm-chart
helm push my-app-0.1.0.tgz oci://registry.example.com/helm-charts

Best Practices

1. Use Values for Everything

Bad: Hardcoded values

replicas: 3

Good: Templated values

replicas: {{ .Values.replicaCount }}

2. Provide Sensible Defaults

replicaCount: 3
resources:
  limits:
    cpu: 1000m
    memory: 1Gi

3. Document Values

# Number of replicas
replicaCount: 3

# Image configuration
image:
  # Image repository
  repository: my-app
  # Image pull policy
  pullPolicy: IfNotPresent

4. Use Semantic Versioning

version: 1.2.3  # MAJOR.MINOR.PATCH

5. Include NOTES.txt

Provide post-install instructions:

1. Get the application URL by running:
{{- if .Values.ingress.enabled }}
  https://{{ .Values.ingress.hosts[0].host }}
{{- else }}
  kubectl port-forward svc/{{ include "chart.fullname" . }} 8080:80
{{- end }}

2. Check the deployment status:
  kubectl get pods -l app.kubernetes.io/name={{ include "chart.name" . }}

Troubleshooting

Debug Template Rendering

helm template my-release ./helm-chart --debug

Check Values

helm get values my-release -n production

Check Manifest

helm get manifest my-release -n production

Common Errors

"template: ... can't evaluate field" - Missing value in values.yaml - Fix: Add default or conditional check

"release ... failed: rendered manifests contain a resource that already exists" - Resource name conflict - Fix: Use helm upgrade instead of install

"Validation failed: ... Invalid value" - Invalid Kubernetes resource - Fix: Check Kubernetes API version and schema

CI/CD Integration

GitHub Actions

name: Deploy with Helm
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Setup Helm
      uses: azure/setup-helm@v3
      with:
        version: 'v3.12.0'

    - name: Deploy
      run: |
        helm upgrade --install my-app ./helm-chart \
          --values production-values.yaml \
          --set image.tag=${{ github.sha }} \
          --namespace production \
          --wait

Next Steps

References