Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

12.4 Creating Your Own Chart

⏱️ 5 min read · 8 min hands-on · 🟡 Intermediate

TL;DR: helm create my-chart scaffolds a complete, working chart with example templates. Strip it down to what you need and fill in your app’s specifics. The scaffold follows best practices (labels, helpers, NOTES.txt) from the start.

After this section you will be able to:

  • Scaffold a new custom chart structure with helm create
  • Parameterize application manifests with reusable variables and templated labels
  • Validate chart syntax and formatting with helm lint

Scaffolding a New Chart

# Create a new chart with all the boilerplate
helm create my-app

# What gets created:
tree my-app
my-app/
├── Chart.yaml
├── values.yaml
├── charts/
└── templates/
    ├── deployment.yaml
    ├── hpa.yaml
    ├── ingress.yaml
    ├── service.yaml
    ├── serviceaccount.yaml
    ├── _helpers.tpl
    ├── NOTES.txt
    └── tests/
        └── test-connection.yaml

The scaffolded chart deploys nginx by default — replace values and templates for your app.


Minimal Chart Walkthrough

After helm create my-app, the key changes to make:

1. Update Chart.yaml:

name: my-app
description: My Application
version: 0.1.0
appVersion: "1.0.0"

2. Update values.yaml defaults:

replicaCount: 2

image:
  repository: myrepo/my-app    # ← Your actual image
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 8080                   # ← Your app's port

env:                           # Custom values section
  LOG_LEVEL: info
  DB_HOST: postgres-svc

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "500m"
    memory: "256Mi"

3. Update templates/deployment.yaml to use your custom env section:

# In the containers section, add env vars from values:
        env:
        {{- range $key, $val := .Values.env }}
        - name: {{ $key }}
          value: {{ $val | quote }}
        {{- end }}

Validating Your Chart

# Lint for common errors
helm lint my-app/

# Expected output (clean):
# ==> Linting my-app/
# [INFO] Chart.yaml: icon is recommended
# 1 chart(s) linted, 0 chart(s) failed

# Preview rendered YAML (catch template errors)
helm template test-release my-app/ | head -80

# Preview with custom values
helm template test-release my-app/ --values my-prod-values.yaml

# Validate against the cluster API without installing
helm template test-release my-app/ | kubectl apply --dry-run=server -f -

Environment-Specific Values Files

# Directory structure for multi-environment deployment
config/
├── values-base.yaml     # Shared defaults
├── values-dev.yaml      # Dev overrides
├── values-staging.yaml  # Staging overrides
└── values-prod.yaml     # Prod overrides
# values-prod.yaml — only what differs from base
replicaCount: 5

resources:
  requests:
    cpu: "500m"
    memory: "512Mi"
  limits:
    cpu: "2"
    memory: "1Gi"

ingress:
  enabled: true
  host: app.mycompany.com

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20
# Deploy to each environment
helm install my-app ./my-app/ \
  --values config/values-base.yaml \
  --values config/values-prod.yaml \
  --namespace production \
  --create-namespace

Packaging and Sharing a Chart

# Package the chart into a .tgz archive
helm package my-app/
# Creates: my-app-0.1.0.tgz

# Install from archive
helm install my-release ./my-app-0.1.0.tgz

# Create a simple file-based chart repository
mkdir -p helm-repo/charts
mv my-app-0.1.0.tgz helm-repo/charts/
helm repo index helm-repo/   # Creates index.yaml

# Host with any static file server (GitHub Pages, S3, etc.)
# Users add it with:
# helm repo add my-team https://my-bucket.s3.amazonaws.com/helm-repo/

Try It

# Create and validate a minimal chart
helm create webapp

# Lint it
helm lint webapp/

# Preview the default nginx install
helm template my-webapp webapp/ | grep "kind:" | sort -u

# Update the image to a different one
sed -i 's|repository: nginx|repository: nginxdemo/hello|' webapp/values.yaml
sed -i 's|tag: ""|tag: "plain-text"|' webapp/values.yaml

# Install it
helm install my-webapp ./webapp/ \
  --namespace helm-create-demo \
  --create-namespace \
  --set service.type=NodePort

helm list -n helm-create-demo
kubectl get all -n helm-create-demo

# Cleanup
helm uninstall my-webapp -n helm-create-demo
kubectl delete namespace helm-create-demo
rm -rf webapp/

Key Takeaways

#ConceptOne-liner
1helm create scaffoldsGenerates a complete working chart in seconds
2helm lint catches errorsRun before every install or push to CI
3helm template previewsSee the final YAML before it touches the cluster
4Multi-values filesBase + environment-specific overrides scale cleanly
5helm package distributesCreates a .tgz for sharing via a chart repo

✅ Quick Check

Q1: helm lint shows no errors but the deployment fails after helm install. What could cause this?

Answer `helm lint` validates chart structure and template syntax, but doesn't check Kubernetes API correctness or cluster-specific rules. The deployment could fail due to: image not existing in the registry, insufficient cluster permissions (RBAC), a node selector with no matching nodes, or an invalid resource reference. Use `helm template | kubectl apply --dry-run=server -f -` for deeper validation against a real cluster API.

Q2: You have values-base.yaml with replicaCount: 2 and values-prod.yaml with replicaCount: 5. Which wins if you pass both with --values?

Answer The last `--values` file wins. `helm install ... --values values-base.yaml --values values-prod.yaml` — `values-prod.yaml` overrides `values-base.yaml`. The replica count is `5`. This is the intentional merge behavior — base first, environment-specific last.

Q3: Can you add custom top-level keys to values.yaml (like env: in the example above) that don’t appear in the upstream chart?

Answer Yes — for your own charts, you define all values yourself, so you can add any structure you want. For third-party charts from repositories, you should only use keys that exist in the chart's `values.yaml` — unknown keys are silently ignored by templates that don't reference them (they don't cause errors, they just have no effect).