Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Give agents more agency

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    foxj77

    helm-chart-development

    foxj77/helm-chart-development
    DevOps

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    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...

    SKILL.md

    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: redis
    

    Values 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: myapp
    

    Template 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-run
    
    Recommended Servers
    MantleKit Launch Planner
    MantleKit Launch Planner
    Laddro Career
    Laddro Career
    Microsoft Learn MCP
    Microsoft Learn MCP
    Repository
    foxj77/claude-code-skills
    Files