Create, test, and package Helm charts for Kubernetes. Covers helm create, Chart.yaml, values.yaml, template development, chart dependencies, packaging, and repository publishing. Use when user mentions Helm charts, helm create, Chart.yaml, values.yaml, helm lint, helm template, helm package, or Kubernetes packaging.
/plugin marketplace add laurigates/claude-plugins/plugin install kubernetes-plugin@lgates-claude-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Comprehensive guidance for creating, testing, and packaging custom Helm charts with best practices for maintainability and reusability.
Use this skill automatically when:
# Scaffold new chart with standard structure
helm create mychart
# Creates:
# mychart/
# ├── Chart.yaml # Chart metadata
# ├── values.yaml # Default values
# ├── charts/ # Chart dependencies
# ├── templates/ # Kubernetes manifests
# │ ├── NOTES.txt # Post-install instructions
# │ ├── _helpers.tpl # Template helpers
# │ ├── deployment.yaml
# │ ├── service.yaml
# │ ├── ingress.yaml
# │ └── tests/
# │ └── test-connection.yaml
# └── .helmignore # Files to ignore
# Chart.yaml - Chart metadata
apiVersion: v2 # Helm 3 uses v2
name: mychart # Chart name
version: 0.1.0 # Chart version (SemVer)
appVersion: "1.0.0" # Application version
description: A Helm chart for Kubernetes
type: application # application or library
keywords:
- api
- web
home: https://example.com
sources:
- https://github.com/example/mychart
maintainers:
- name: John Doe
email: john@example.com
dependencies: # Chart dependencies
- name: postgresql
version: "12.1.9"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled # Optional: enable/disable
tags: # Optional: group dependencies
- database
# values.yaml - Default configuration
# Use clear hierarchy and comments
# Replica configuration
replicaCount: 1
# Image configuration
image:
repository: nginx
pullPolicy: IfNotPresent
tag: "" # Overrides appVersion
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
# Service configuration
service:
type: ClusterIP
port: 80
# Ingress configuration
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# Resource limits
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
# Autoscaling
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# Node selection
nodeSelector: {}
tolerations: []
affinity: {}
# Basic linting
helm lint ./mychart
# Strict linting (warnings as errors)
helm lint ./mychart --strict
# Lint with specific values
helm lint ./mychart \
--values values.yaml \
--strict
# Lint with multiple value files
helm lint ./mychart \
--values values/common.yaml \
--values values/production.yaml \
--strict
What Lint Checks:
# Render all templates
helm template mychart ./mychart
# Render with custom release name and namespace
helm template myrelease ./mychart \
--namespace production
# Render with values
helm template myrelease ./mychart \
--values values.yaml
# Render specific template
helm template myrelease ./mychart \
--show-only templates/deployment.yaml
# Render with debug output
helm template myrelease ./mychart \
--debug
# Validate against Kubernetes API
helm template myrelease ./mychart \
--validate
# Dry-run with server-side validation
helm install myrelease ./mychart \
--namespace production \
--dry-run \
--debug
# Validates:
# - Template rendering
# - Kubernetes API compatibility
# - Resource conflicts
# - RBAC permissions
# Install chart
helm install myrelease ./mychart --namespace test
# Run tests
helm test myrelease --namespace test
# Run tests with logs
helm test myrelease --namespace test --logs
# Cleanup after tests
helm uninstall myrelease --namespace test
Chart Test Structure:
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "mychart.fullname" . }}-test-connection"
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": hook-succeeded,hook-failed
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a fully qualified app name.
*/}}
{{- define "mychart.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 "mychart.labels" -}}
helm.sh/chart: {{ include "mychart.chart" . }}
{{ include "mychart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
Use Named Templates:
# ✅ GOOD: Reusable labels
labels:
{{- include "mychart.labels" . | nindent 2 }}
# ❌ BAD: Duplicate label definitions
labels:
app.kubernetes.io/name: mychart
app.kubernetes.io/instance: {{ .Release.Name }}
Quote String Values:
# ✅ GOOD: Quoted to prevent YAML issues
env:
- name: APP_NAME
value: {{ .Values.appName | quote }}
# ❌ BAD: Unquoted strings can break
env:
- name: APP_NAME
value: {{ .Values.appName }} # Breaks if value is "true" or "123"
Use Required for Mandatory Values:
# ✅ GOOD: Fails fast with clear error
database:
host: {{ required "database.host is required" .Values.database.host }}
# ❌ BAD: Silent failure or confusing errors
database:
host: {{ .Values.database.host }}
Handle Whitespace Properly:
# ✅ GOOD: Proper indentation and chomping
labels:
{{- include "mychart.labels" . | nindent 2 }}
# ❌ BAD: Extra newlines or indentation issues
labels:
{{ include "mychart.labels" . }}
Conditional Resources:
# ✅ GOOD: Clean conditional
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "mychart.fullname" . }}
spec:
...
{{- end }}
# ❌ BAD: Always creates resource
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "mychart.fullname" . }}
spec:
{{- if .Values.ingress.enabled }}
...
{{- end }}
# Chart.yaml
dependencies:
- name: postgresql
version: "12.1.9"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled # Enable/disable via values
- name: redis
version: "17.0.0"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
- name: common # Local dependency
version: "1.0.0"
repository: file://../common-library
# Download/update dependencies
helm dependency update ./mychart
# Creates:
# - Chart.lock # Locked versions
# - charts/*.tgz # Downloaded charts
# Build from existing Chart.lock
helm dependency build ./mychart
# List dependencies
helm dependency list ./mychart
# values.yaml - Parent chart
# Configure subcharts using subchart name as key
# PostgreSQL subchart configuration
postgresql:
enabled: true
auth:
username: myapp
database: myapp
existingSecret: myapp-db-secret
primary:
persistence:
size: 10Gi
# Redis subchart configuration
redis:
enabled: true
auth:
enabled: false
master:
persistence:
size: 5Gi
# Override subchart values from CLI
helm install myapp ./mychart \
--set postgresql.auth.password=secret123 \
--set redis.enabled=false
# Package chart into .tgz
helm package ./mychart
# Creates: mychart-0.1.0.tgz
# Package with specific destination
helm package ./mychart --destination ./dist/
# Package and update dependencies
helm package ./mychart --dependency-update
# Sign package (requires GPG key)
helm package ./mychart --sign --key mykey --keyring ~/.gnupg/secring.gpg
# Create repository index
helm repo index ./repo/
# Creates: index.yaml
# Add local repository
helm repo add myrepo file://$(pwd)/repo
# Update repository index
helm repo update
# Search local repository
helm search repo myrepo/
# Push to ChartMuseum (if using)
curl --data-binary "@mychart-0.1.0.tgz" http://chartmuseum.example.com/api/charts
# Push to OCI registry (Helm 3.8+)
helm push mychart-0.1.0.tgz oci://registry.example.com/charts
{
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "MyChart Values",
"type": "object",
"required": ["image", "service"],
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "Number of replicas"
},
"image": {
"type": "object",
"required": ["repository", "tag"],
"properties": {
"repository": {
"type": "string",
"description": "Image repository"
},
"tag": {
"type": "string",
"pattern": "^v?[0-9]+\\.[0-9]+\\.[0-9]+$",
"description": "Image tag (SemVer)"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"],
"description": "Image pull policy"
}
}
},
"service": {
"type": "object",
"required": ["port"],
"properties": {
"type": {
"type": "string",
"enum": ["ClusterIP", "NodePort", "LoadBalancer"],
"description": "Service type"
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"description": "Service port"
}
}
},
"ingress": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable ingress"
},
"hosts": {
"type": "array",
"items": {
"type": "object",
"required": ["host"],
"properties": {
"host": {
"type": "string",
"format": "hostname"
}
}
}
}
}
}
}
}
Schema validation runs automatically during:
helm installhelm upgradehelm template --validatehelm lint# Validation errors will show:
Error: values don't meet the specifications of the schema(s)
- replicaCount: Invalid type. Expected: integer, given: string
# templates/NOTES.txt - Post-install instructions
Thank you for installing {{ .Chart.Name }}!
Your release is named {{ .Release.Name }}.
To learn more about the release, try:
$ helm status {{ .Release.Name }} --namespace {{ .Release.Namespace }}
$ helm get all {{ .Release.Name }} --namespace {{ .Release.Namespace }}
{{- if .Values.ingress.enabled }}
Application is available at:
{{- range .Values.ingress.hosts }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }}
{{- end }}
{{- else }}
Get the application URL by running:
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "mychart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:{{ .Values.service.port }}
echo "Visit http://127.0.0.1:8080"
{{- end }}
# MyChart
A Helm chart for deploying MyApp to Kubernetes.
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
## Installation
```bash
helm install myapp oci://registry.example.com/charts/mychart
See values.yaml in your chart directory for configuration options.
Key parameters:
replicaCount - Number of replicas (default: 1)image.repository - Image repositoryimage.tag - Image taghelm upgrade myapp oci://registry.example.com/charts/mychart
helm uninstall myapp
## Chart Testing Workflow
### Local Development Testing
```bash
# 1. Create chart
helm create testchart
# 2. Modify templates and values
# Edit templates/deployment.yaml, values.yaml
# 3. Lint
helm lint ./testchart --strict
# 4. Render templates
helm template testapp ./testchart \
--values test-values.yaml \
--debug
# 5. Dry-run
helm install testapp ./testchart \
--namespace test \
--create-namespace \
--dry-run \
--debug
# 6. Install to test cluster
helm install testapp ./testchart \
--namespace test \
--create-namespace \
--atomic \
--wait
# 7. Run tests
helm test testapp --namespace test --logs
# 8. Verify deployment
kubectl get all -n test -l app.kubernetes.io/instance=testapp
# 9. Cleanup
helm uninstall testapp --namespace test
kubectl delete namespace test
# GitHub Actions example
name: Chart Testing
on: [pull_request]
jobs:
lint-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Helm
uses: azure/setup-helm@v3
with:
version: '3.12.0'
- name: Lint Chart
run: helm lint ./charts/mychart --strict
- name: Template Chart
run: |
helm template test ./charts/mychart \
--values ./charts/mychart/ci/test-values.yaml \
--validate
- name: Install Chart Testing
uses: helm/chart-testing-action@v2
- name: Run Chart Tests
run: ct lint-and-install --charts ./charts/mychart
# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "mychart.fullname" . }}
data:
{{- range $key, $value := .Values.config }}
{{ $key }}: {{ $value | quote }}
{{- end }}
# values.yaml
config:
app.name: "MyApp"
log.level: "info"
feature.enabled: "true"
# templates/deployment.yaml
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.existingSecret | default (include "mychart.fullname" .) }}
key: db-password
# values.yaml
services:
api:
port: 8080
type: ClusterIP
metrics:
port: 9090
type: ClusterIP
# templates/services.yaml
{{- range $name, $config := .Values.services }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "mychart.fullname" $ }}-{{ $name }}
spec:
type: {{ $config.type }}
ports:
- port: {{ $config.port }}
targetPort: {{ $config.port }}
selector:
{{- include "mychart.selectorLabels" $ | nindent 4 }}
{{- end }}
✅ DO: Use SemVer for chart and app versions
# Chart.yaml
version: 1.2.3 # Chart version
appVersion: "2.5.0" # Application version
✅ DO: Use consistent naming functions
name: {{ include "mychart.fullname" . }}
labels:
{{- include "mychart.labels" . | nindent 2 }}
✅ DO: Provide sensible defaults in values.yaml
replicaCount: 1 # Safe default for testing
resources:
limits:
cpu: 100m
memory: 128Mi # Reasonable defaults
✅ DO: Comment values.yaml extensively
# Number of replicas to deploy
# Recommended: 3+ for production
replicaCount: 1
✅ DO: Include chart tests
# Always include templates/tests/
helm test <release>
This skill should be used when the user asks about libraries, frameworks, API references, or needs code examples. Activates for setup questions, code generation involving libraries, or mentions of specific frameworks like React, Vue, Next.js, Prisma, Supabase, etc.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.