helm-chart-development
Skill19 starsChanged 10 months agoMITView on GitHub
Use when creating new Helm charts from scratch, designing values.yaml schema and structure, implementing Helm template patterns, setting up chart directory structure, writing template helpers (_helpers.tpl), or creating values.schema.json validation
- Reads credentials
1 other project uses a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- community-charts/helm-chartsedited, first seen Oct 8, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 1
- community-charts/helm-charts★ 13343% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository19 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- cert-manager-troubleshootingSkill
- external-dns-troubleshootingSkill
- external-secrets-troubleshootingSkill
- flux-gitops-patternsSkill
- flux-operationsSkill
- flux-troubleshootingSkill
- helm-chart-maintenanceSkill
- helm-chart-reviewSkill
- helm-chart-testingSkill
- k8s-continual-improvementSkill
- k8s-dns-troubleshootingSkill
- k8s-namespace-troubleshootingSkill
- k8s-network-troubleshootingSkill
- k8s-platform-operationsSkill
- k8s-platform-tenancySkill
- k8s-security-hardeningSkill
- k8s-security-redteamSkill
- kyverno-troubleshootingSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 8
Create and structure Helm charts following best practices, including templating, values design, and chart architecture.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Helm Chart Development
Create and structure Helm charts following best practices, including templating, values design, and chart architecture.
Keywords
helm, chart, development, templating, template, values, schema, kubernetes, packaging, creating, create, scaffold, scaffolding, helpers, deployment, deploying, writing, designing, structure
When to Use This Skill
- Creating new Helm charts from scratch
- Designing values.yaml schema and structure
- Implementing Helm template patterns
- Setting up chart directory structure
- Writing template helpers (_helpers.tpl)
- Creating values.schema.json validation
Related Skills
- helm-chart-maintenance - Versioning and updates
- helm-chart-review - Quality and security review
- k8s-security-hardening - Security best practices
- Shared: Pod Security Context
Quick Reference
| Task | Command |
| Create chart | helm create mychart |
| Lint chart | helm lint mychart/ |
| Template dry-run | helm template myrelease mychart/ -f values.yaml |
| Package chart | helm package mychart/ |
| Install debug | helm install myrelease mychart/ --debug --dry-run |
Chart Structure
Standard Layout
mychart/
├── Chart.yaml
├── Chart.lock
├── values.yaml
├── values.schema.json
├── README.md
├── LICENSE
├── .helmignore
├── templates/
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── serviceaccount.yaml
│ ├── hpa.yaml
│ └── tests/
│ └── test-connection.yaml
├── charts/
└── crds/Chart.yaml
Complete Example
apiVersion: v2
name: myapp
description: A Helm chart for MyApp
type: application
version: 1.0.0
appVersion: "2.3.1"
home: https://github.com/org/myapp
icon: https://example.com/icon.png
sources:
- https://github.com/org/myapp
maintainers:
- name: Platform Team
email: platform@company.com
url: https://platform.company.com
keywords:
- app
- web
annotations:
artifacthub.io/changes: |
- kind: added
description: Initial release
artifacthub.io/license: Apache-2.0
dependencies:
- name: postgresql
version: "12.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
tags:
- database
- name: redis
version: "17.x.x"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
import-values:
- child: primary
parent: redisValues Design
Well-Structured values.yaml
# -- Number of replicas
replicaCount: 1
image:
# -- Image repository
repository: myapp
# -- Image pull policy
pullPolicy: IfNotPresent
# -- Image tag (defaults to appVersion)
tag: ""
# -- Image pull secrets
imagePullSecrets: []
# -- Override chart name
nameOverride: ""
# -- Override full name
fullnameOverride: ""
serviceAccount:
# -- Create service account
create: true
# -- Service account annotations
annotations: {}
# -- Service account name (generated if not set)
name: ""
# -- Automount service account token
automount: true
# -- Pod annotations
podAnnotations: {}
# -- Pod labels
podLabels: {}
podSecurityContext:
fsGroup: 1000
runAsNonRoot: true
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
service:
# -- Service type
type: ClusterIP
# -- Service port
port: 80
# -- Target port
targetPort: 8080
# -- Node port (when type is NodePort)
nodePort: ""
ingress:
# -- Enable ingress
enabled: false
# -- Ingress class name
className: ""
# -- Ingress annotations
annotations: {}
# -- Ingress hosts
hosts:
- host: chart-example.local
paths:
- path: /
pathType: Prefix
# -- Ingress TLS configuration
tls: []
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# -- Node selector
nodeSelector: {}
# -- Tolerations
tolerations: []
# -- Affinity rules
affinity: {}
# -- Extra environment variables
extraEnv: []
# -- Extra volume mounts
extraVolumeMounts: []
# -- Extra volumes
extraVolumes: []
# -- Liveness probe configuration
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 10
# -- Readiness probe configuration
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
# Subchart configuration
postgresql:
enabled: false
auth:
database: myapp
username: myappTemplate Helpers (_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 }}
{{/*
Create chart name and version for chart label.
*/}}
{{- define "myapp.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "myapp.labels" -}}
helm.sh/chart: {{ include "myapp.chart" . }}
{{ include "myapp.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "myapp.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "myapp.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Return the proper image name
*/}}
{{- define "myapp.image" -}}
{{- $tag := .Values.image.tag | default .Chart.AppVersion -}}
{{- printf "%s:%s" .Values.image.repository $tag }}
{{- end }}Template Patterns
Deployment Template
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "myapp.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "myapp.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "myapp.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: {{ include "myapp.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.extraEnv }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.extraVolumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.extraVolumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}Conditional Resource
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "myapp.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}Values Schema (values.schema.json)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["image"],
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 1,
"description": "Number of replicas"
},
"image": {
"type": "object",
"required": ["repository"],
"properties": {
"repository": {
"type": "string",
"description": "Image repository"
},
"tag": {
"type": "string",
"description": "Image tag"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"],
"default": "IfNotPresent"
}
}
},
"resources": {
"type": "object",
"properties": {
"limits": {
"type": "object",
"properties": {
"cpu": { "type": "string" },
"memory": { "type": "string" }
}
},
"requests": {
"type": "object",
"properties": {
"cpu": { "type": "string" },
"memory": { "type": "string" }
}
}
}
}
}
}NOTES.txt Template
{{- $fullName := include "myapp.fullname" . -}}
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ $fullName }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
Watch status: kubectl get --namespace {{ .Release.Namespace }} svc -w {{ $fullName }}
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ $fullName }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ $fullName }} 8080:{{ .Values.service.port }}
echo "Visit http://127.0.0.1:8080"
{{- end }}Templating Tips
Whitespace Control
# Use {{- and -}} to trim whitespace
{{- if .Values.enabled }}
key: value
{{- end }}
# Use nindent for proper indentation
labels:
{{- include "myapp.labels" . | nindent 2 }}Required Values
# Fail if value not set
image: {{ required "image.repository is required" .Values.image.repository }}Default Values
# Provide default
port: {{ .Values.port | default 8080 }}
# Coalesce (first non-empty)
name: {{ coalesce .Values.name .Values.nameOverride .Chart.Name }}Loops and Ranges
# Range over list
{{- range .Values.hosts }}
- {{ . | quote }}
{{- end }}
# Range with index
{{- range $index, $host := .Values.hosts }}
- name: host-{{ $index }}
value: {{ $host }}
{{- end }}
# Range over map
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}CLI Commands
# Create new chart
helm create mychart
# Lint chart
helm lint mychart/
# Template locally (dry-run)
helm template myrelease mychart/ -f values.yaml
# Package chart
helm package mychart/
# Install with debug
helm install myrelease mychart/ --debug --dry-runDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

