helm-chart-scaffolding

Comprehensive Helm chart creation, templating, and packaging guidance for Kubernetes applications. Covers full chart lifecycle: initialization, Chart.yaml configuration, values.yaml design, template creation with Go templating, and dependency management Includes multi-environment deployment patterns with environment-specific values files (dev, staging, prod) and conditional resource rendering Provides validation, testing, and packaging workflows with linting, dry-run testing, and chart repository setup Demonstrates common templating patterns: conditionals, loops, file inclusion, and global values; plus pre-install hooks and test pods

INSTALLATION
npx skills add https://github.com/wshobson/agents --skill helm-chart-scaffolding
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Helm Chart Scaffolding

Comprehensive guidance for creating, organizing, and managing Helm charts for packaging and deploying Kubernetes applications.

Purpose

This skill provides step-by-step instructions for building production-ready Helm charts, including chart structure, templating patterns, values management, and validation strategies.

When to Use This Skill

Use this skill when you need to:

  • Create new Helm charts from scratch
  • Package Kubernetes applications for distribution
  • Manage multi-environment deployments with Helm
  • Implement templating for reusable Kubernetes manifests
  • Set up Helm chart repositories
  • Follow Helm best practices and conventions

Helm Overview

Helm is the package manager for Kubernetes that:

  • Templates Kubernetes manifests for reusability
  • Manages application releases and rollbacks
  • Handles dependencies between charts
  • Provides version control for deployments
  • Simplifies configuration management across environments

Step-by-Step Workflow

1. Initialize Chart Structure

Create new chart:

helm create my-app

Standard chart structure:

my-app/

├── Chart.yaml           # Chart metadata

├── values.yaml          # Default configuration values

├── charts/              # Chart dependencies

├── templates/           # Kubernetes manifest templates

│   ├── NOTES.txt       # Post-install notes

│   ├── _helpers.tpl    # Template helpers

│   ├── deployment.yaml

│   ├── service.yaml

│   ├── ingress.yaml

│   ├── serviceaccount.yaml

│   ├── hpa.yaml

│   └── tests/

│       └── test-connection.yaml

└── .helmignore         # Files to ignore

2. Configure Chart.yaml

Chart metadata defines the package:

apiVersion: v2

name: my-app

description: A Helm chart for My Application

type: application

version: 1.0.0 # Chart version

appVersion: "2.1.0" # Application version

# Keywords for chart discovery

keywords:

  - web

  - api

  - backend

# Maintainer information

maintainers:

  - name: DevOps Team

    email: devops@example.com

    url: https://github.com/example/my-app

# Source code repository

sources:

  - https://github.com/example/my-app

# Homepage

home: https://example.com

# Chart icon

icon: https://example.com/icon.png

# Dependencies

dependencies:

  - name: postgresql

    version: "12.0.0"

    repository: "https://charts.bitnami.com/bitnami"

    condition: postgresql.enabled

  - name: redis

    version: "17.0.0"

    repository: "https://charts.bitnami.com/bitnami"

    condition: redis.enabled

Reference: See assets/Chart.yaml.template for complete example

3. Design values.yaml Structure

Organize values hierarchically:

# Image configuration

image:

  repository: myapp

  tag: "1.0.0"

  pullPolicy: IfNotPresent

# Number of replicas

replicaCount: 3

# Service configuration

service:

  type: ClusterIP

  port: 80

  targetPort: 8080

# Ingress configuration

ingress:

  enabled: false

  className: nginx

  hosts:

    - host: app.example.com

      paths:

        - path: /

          pathType: Prefix

# Resources

resources:

  requests:

    memory: "256Mi"

    cpu: "250m"

  limits:

    memory: "512Mi"

    cpu: "500m"

# Autoscaling

autoscaling:

  enabled: false

  minReplicas: 2

  maxReplicas: 10

  targetCPUUtilizationPercentage: 80

# Environment variables

env:

  - name: LOG_LEVEL

    value: "info"

# ConfigMap data

configMap:

  data:

    APP_MODE: production

# Dependencies

postgresql:

  enabled: true

  auth:

    database: myapp

    username: myapp

redis:

  enabled: false

Reference: See assets/values.yaml.template for complete structure

4. Create Template Files

Use Go templating with Helm functions:

templates/deployment.yaml:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: {{ include "my-app.fullname" . }}

  labels:

    {{- include "my-app.labels" . | nindent 4 }}

spec:

  {{- if not .Values.autoscaling.enabled }}

  replicas: {{ .Values.replicaCount }}

  {{- end }}

  selector:

    matchLabels:

      {{- include "my-app.selectorLabels" . | nindent 6 }}

  template:

    metadata:

      labels:

        {{- include "my-app.selectorLabels" . | nindent 8 }}

    spec:

      containers:

      - name: {{ .Chart.Name }}

        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"

        imagePullPolicy: {{ .Values.image.pullPolicy }}

        ports:

        - name: http

          containerPort: {{ .Values.service.targetPort }}

        resources:

          {{- toYaml .Values.resources | nindent 12 }}

        env:

          {{- toYaml .Values.env | nindent 12 }}

5. Create Template Helpers

templates/_helpers.tpl:

{{/*

Expand the name of the chart.

*/}}

{{- define "my-app.name" -}}

{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}

{{- end }}

{{/*

Create a default fully qualified app name.

*/}}

{{- define "my-app.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 }}

{{/*

Common labels

*/}}

{{- define "my-app.labels" -}}

helm.sh/chart: {{ include "my-app.chart" . }}

{{ include "my-app.selectorLabels" . }}

{{- if .Chart.AppVersion }}

app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}

{{- end }}

app.kubernetes.io/managed-by: {{ .Release.Service }}

{{- end }}

{{/*

Selector labels

*/}}

{{- define "my-app.selectorLabels" -}}

app.kubernetes.io/name: {{ include "my-app.name" . }}

app.kubernetes.io/instance: {{ .Release.Name }}

{{- end }}

6. Manage Dependencies

Add dependencies in Chart.yaml:

dependencies:

  - name: postgresql

    version: "12.0.0"

    repository: "https://charts.bitnami.com/bitnami"

    condition: postgresql.enabled

Update dependencies:

helm dependency update

helm dependency build

Override dependency values:

# values.yaml

postgresql:

  enabled: true

  auth:

    database: myapp

    username: myapp

    password: changeme

  primary:

    persistence:

      enabled: true

      size: 10Gi

7. Test and Validate

Validation commands:

# Lint the chart

helm lint my-app/

# Dry-run installation

helm install my-app ./my-app --dry-run --debug

# Template rendering

helm template my-app ./my-app

# Template with values

helm template my-app ./my-app -f values-prod.yaml

# Show computed values

helm show values ./my-app

Validation script:

#!/bin/bash

set -e

echo "Linting chart..."

helm lint .

echo "Testing template rendering..."

helm template test-release . --dry-run

echo "Checking for required values..."

helm template test-release . --validate

echo "All validations passed!"

Reference: See scripts/validate-chart.sh

8. Package and Distribute

Package the chart:

helm package my-app/

# Creates: my-app-1.0.0.tgz

Create chart repository:

# Create index

helm repo index .

# Upload to repository

# AWS S3 example

aws s3 sync . s3://my-helm-charts/ --exclude "*" --include "*.tgz" --include "index.yaml"

Use the chart:

helm repo add my-repo https://charts.example.com

helm repo update

helm install my-app my-repo/my-app

9. Multi-Environment Configuration

Environment-specific values files:

my-app/

├── values.yaml          # Defaults

├── values-dev.yaml      # Development

├── values-staging.yaml  # Staging

└── values-prod.yaml     # Production

values-prod.yaml:

replicaCount: 5

image:

  tag: "2.1.0"

resources:

  requests:

    memory: "512Mi"

    cpu: "500m"

  limits:

    memory: "1Gi"

    cpu: "1000m"

autoscaling:

  enabled: true

  minReplicas: 3

  maxReplicas: 20

ingress:

  enabled: true

  hosts:

    - host: app.example.com

      paths:

        - path: /

          pathType: Prefix

postgresql:

  enabled: true

  primary:

    persistence:

      size: 100Gi

Install with environment:

helm install my-app ./my-app -f values-prod.yaml --namespace production

10. Implement Hooks and Tests

Pre-install hook:

# templates/pre-install-job.yaml

apiVersion: batch/v1

kind: Job

metadata:

  name: {{ include "my-app.fullname" . }}-db-setup

  annotations:

    "helm.sh/hook": pre-install

    "helm.sh/hook-weight": "-5"

    "helm.sh/hook-delete-policy": hook-succeeded

spec:

  template:

    spec:

      containers:

      - name: db-setup

        image: postgres:15

        command: ["psql", "-c", "CREATE DATABASE myapp"]

      restartPolicy: Never

Test connection:

# templates/tests/test-connection.yaml

apiVersion: v1

kind: Pod

metadata:

  name: "{{ include "my-app.fullname" . }}-test-connection"

  annotations:

    "helm.sh/hook": test

spec:

  containers:

  - name: wget

    image: busybox

    command: ['wget']

    args: ['{{ include "my-app.fullname" . }}:{{ .Values.service.port }}']

  restartPolicy: Never

Run tests:

helm test my-app

Common Patterns

Pattern 1: Conditional Resources

{{- if .Values.ingress.enabled }}

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

  name: {{ include "my-app.fullname" . }}

spec:

  # ...

{{- end }}

Pattern 2: Iterating Over Lists

env:

{{- range .Values.env }}

- name: {{ .name }}

  value: {{ .value | quote }}

{{- end }}

Pattern 3: Including Files

data:

  config.yaml: |

    {{- .Files.Get "config/application.yaml" | nindent 4 }}

Pattern 4: Global Values

global:

  imageRegistry: docker.io

  imagePullSecrets:

    - name: regcred

# Use in templates:

image: {{ .Values.global.imageRegistry }}/{{ .Values.image.repository }}

Best Practices

  • Use semantic versioning for chart and app versions
  • Document all values in values.yaml with comments
  • Use template helpers for repeated logic
  • Validate charts before packaging
  • Pin dependency versions explicitly
  • Use conditions for optional resources
  • Follow naming conventions (lowercase, hyphens)
  • Include NOTES.txt with usage instructions
  • Add labels consistently using helpers
  • Test installations in all environments

Troubleshooting

Template rendering errors:

helm template my-app ./my-app --debug

Dependency issues:

helm dependency update

helm dependency list

Installation failures:

helm install my-app ./my-app --dry-run --debug

kubectl get events --sort-by='.lastTimestamp'

Related Skills

  • k8s-manifest-generator - For creating base Kubernetes manifests
  • gitops-workflow - For automated Helm chart deployments
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card