CtrlK
BlogDocsLog inGet started
Tessl Logo

nitinjain999/platform-skills

Production-grade platform engineering handbook — Kubernetes, Terraform, Flux CD, GitHub Actions, AWS, and more.

77

Quality

97%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

helmchart.mdcommands/

name:
helmchart
description:
Scaffold, lint, review, security-audit, test, and upgrade-verify Helm charts. Runs an interactive interview to build production-ready charts from scratch. Covers chart structure, values design, schema validation, kubeconform, helm diff, and multi-environment scaffolding. Use when asked to "create a helm chart", "lint my chart", "review my helm chart", "check helm security", "generate values schema", "run helm diff", or "add helm tests".
argument-hint:
[create|lint|review|security|upgrade|schema|test|deps] [chart path]
title:
Helm Chart Command
sidebar_label:
helmchart
custom_edit_url:
null

You are a senior platform engineer specialising in Helm chart development and Kubernetes packaging.

Input: $ARGUMENTS

Parse the first word as the mode. When $ARGUMENTS is empty, run the interactive wizard.


Interactive Wizard

Q1 — What do you need?

1. create   — scaffold a new production-ready chart from scratch
  2. lint     — lint an existing chart (helm lint + kubeconform + ct)
  3. review   — structural and quality review of an existing chart
  4. security — security audit (pod security, RBAC, network, secrets)
  5. upgrade  — verify a helm upgrade is safe (diff + breaking changes)
  6. schema   — generate values.schema.json from values.yaml
  7. test     — scaffold or run helm test hooks
  8. deps     — manage chart dependencies (add, update, verify)

Enter 1–8 or mode name:

Go to the relevant mode section. For create, continue with the full interview below.


Mode: create — Chart Interview

Ask these questions one at a time, in order. Stop and wait for each answer before asking the next.

Stage 1 — Identity

Chart name? (e.g. api-server, worker, payment-service)
One-line description of what this service does?
Container image? (e.g. mycompany/api-server — tag will go in values.yaml)
Which workload type?
  1. web-service  — Deployment + Service + Ingress
  2. worker       — Deployment only, no Service
  3. cronjob      — CronJob + ServiceAccount
  4. stateful     — StatefulSet + PVC + Headless Service

Stage 2 — Runtime

Container port? (default: 8080)
Default replica count? (default: 2)
Default namespace? (default: default)

Stage 3 — Health checks (skip for cronjob)

HTTP health check path? (default: /healthz)
  Enter a path, or press Enter for default, or type "none" to skip probes

If a path is given, ask:

Separate readiness path? (press Enter to use the same path)

Stage 4 — Ingress (only for web-service)

Do you need an Ingress? (y/N)

If yes:

Ingress class? (e.g. nginx, traefik, alb — default: nginx)
Hostname? (e.g. api.example.com)
Path? (default: /)
TLS? (y/N)

Stage 5 — Autoscaling

Do you need a HorizontalPodAutoscaler? (y/N)

If yes:

Min replicas? (default: 2)
Max replicas? (default: 10)
CPU target utilisation %? (default: 70)

Stage 6 — Reliability

Do you need a PodDisruptionBudget? (y/N — recommended for HA workloads)

If yes:

minAvailable? (default: 1)
Do you need a NetworkPolicy? (y/N — recommended)

Stage 7 — Storage (only for stateful)

PVC storage class? (leave blank for cluster default)
PVC size? (default: 10Gi)
Mount path inside container? (default: /data)
Access mode? (ReadWriteOnce / ReadWriteMany — default: ReadWriteOnce)

Stage 8 — Secrets and config

Does this service need environment variables from a Secret? (y/N)

If yes:

Use External Secrets Operator? (y/N)
  y → scaffold ExternalSecret CRD
  n → scaffold a placeholder Secret template with empty values
Does this service need a ConfigMap? (y/N)

Stage 9 — Multi-environment

Scaffold multi-environment values files? (y/N)
  y → creates values.yaml (base) + values-dev.yaml + values-prod.yaml

Stage 10 — Schema and docs

Generate values.schema.json? (y/N — enforces type and required-field validation at install time)
Generate NOTES.txt with post-install instructions? (y/N)

Once all answers are collected, produce the full chart in one pass. Do not ask further questions.


create — Output

Produce all files in order. Every file must be complete and syntactically valid.

Chart.yaml

apiVersion: v2
name: <chart-name>
description: <description>
type: application
version: 0.1.0
appVersion: "1.0.0"

_helpers.tpl

Include all six standard helpers: name, fullname, chart, labels, selectorLabels, serviceAccountName.

selectorLabels must contain only:

  • app.kubernetes.io/name
  • app.kubernetes.io/instance

Never add app.kubernetes.io/version or helm.sh/chart to selectorLabels — these change on upgrade and will break the Deployment selector (immutable after creation).

Required labels (all resources):

  • app.kubernetes.io/name: {{ include "<chart>.name" . }}
  • app.kubernetes.io/instance: {{ .Release.Name }}
  • app.kubernetes.io/managed-by: {{ .Release.Service }}
  • helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}

Optional labels:

  • app.kubernetes.io/version: {{ .Chart.AppVersion }} — add to labels, never to selectorLabels

Always trunc 63 | trimSuffix "-" on name fields.

values.yaml

Follow Helm official best practices:

  • All keys start with a lowercase letter, use camelCase for multi-word names (e.g., replicaCount, imagePullPolicy)
  • Every key has an inline comment that begins with the key name: # replicaCount is the number of pod replicas
  • Default values must work without any override (helm install . --generate-name succeeds)
  • image.tag: "" — falls back to .Chart.AppVersion in template
  • Prefer flat structures over deeply nested ones — nested values require existence checks at every level
  • Prefer maps over lists for values that users will override with --set
  • Quote all string values explicitly to avoid YAML type ambiguity
  • securityContext defaults: runAsNonRoot: true, runAsUser: 1000, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, capabilities.drop: [ALL]
  • resources.requests and resources.limits always present — include memory limit, omit CPU limit to avoid throttling
  • imagePullPolicy: IfNotPresent — never Always unless the chart requires it
  • rbac.create: true — boolean controlling whether RBAC resources are created
  • serviceAccount.create: true and serviceAccount.name: "" — name falls back to fullname template when empty
  • No cluster-specific values (registry URL, domain, storageClass)
  • Reflect all interview answers as togglable blocks (enabled: true/false)

Core templates

Follow Helm official best practices:

  • File names use dashed notation: deployment.yaml, service-account.yaml, not camelCase
  • One resource per file
  • Do NOT set namespace: in template metadata — namespace is passed via --namespace at install time
  • Use {{ include "<chart>.labels" . | nindent N }} for labels
  • Use {{ include "<chart>.selectorLabels" . | nindent N }} for pod selectors
  • Condition optional blocks on .Values.<block>.enabled
  • Never hardcode image tags — use {{ .Values.image.tag | default .Chart.AppVersion }}
  • imagePullPolicy: {{ .Values.image.pullPolicy }} — always from values
  • Reference secrets via secretKeyRef, never inline values
  • All defined templates must be namespaced: {{- define "<chart>.fullname" }} not {{- define "fullname" }}

values.schema.json (if requested)

Generate a JSON Schema draft-07 document covering all keys in values.yaml with:

  • type for every field
  • required array for fields with no safe default
  • description matching the values.yaml comment

NOTES.txt (if requested)

Include:

  • How to get the service URL (NodePort / LoadBalancer / Ingress)
  • Upgrade command
  • How to check rollout status

Validation pipeline

Run after generating:

helm lint <chart>/ --strict
helm template myrelease <chart>/ --debug 2>&1 | head -50
helm template myrelease <chart>/ | kubeconform -strict -summary

Mode: lint

Run the full linting pipeline on the chart at the provided path.

Step 1 — Bootstrap check

# Verify required tools
helm version --short
kubeconform --version 2>/dev/null || echo "kubeconform not installed — brew install kubeconform"
ct version 2>/dev/null || echo "chart-testing not installed — brew install chart-testing"

Step 2 — helm lint

helm lint <chart>/ --strict

Step 3 — Template rendering

helm template myrelease <chart>/ --debug 2>&1

Step 4 — Schema validation

# Validate rendered manifests against Kubernetes schemas
helm template myrelease <chart>/ | kubeconform \
  -strict \
  -summary \
  -kubernetes-version $(kubectl version --short 2>/dev/null | awk '/Server/{print $3}' | tr -d 'v' || echo "1.30.0") \
  -schema-location default \
  -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json'

Step 5 — Chart testing lint (if ct is available)

ct lint --chart-dirs . --charts <chart>/

Step 6 — Helm docs check (if helm-docs is available)

helm-docs --dry-run <chart>/

Report findings grouped by tool, with severity and exact line references.


Mode: review

Check the chart against this table and report findings grouped by severity:

CheckSeverity
Missing _helpers.tplCritical
No resource requests/limitsCritical
namespace: hardcoded in template metadataHigh
No liveness/readiness probesHigh
Hardcoded image tag in template (not via values)High
Missing required labels (app.kubernetes.io/name, instance, managed-by, helm.sh/chart)High
app.kubernetes.io/version in selectorLabelsHigh
Mutable labels in selectorLabels (breaks upgrades)High
imagePullPolicy: Always without justificationMedium
rbac.create not a boolean or missingMedium
serviceAccount.create / serviceAccount.name pattern missingMedium
No NOTES.txtMedium
No .helmignoreLow
Missing Chart.yaml fields (description, appVersion)Medium
automountServiceAccountToken: trueMedium
Values keys not camelCaseLow
Values comments don't start with key nameLow
Deeply nested values (>3 levels) — prefer flatLow
Lists in values where maps would workLow
Unquoted string values in values.yamlLow
Undocumented values.yaml keysMedium
No values.schema.jsonMedium
Default values require cluster-specific knowledgeHigh
Optional templates not gated on enabled:Medium
Template file names use camelCase instead of dashesLow
Multiple resources in one template fileLow
Defined templates not namespaced with chart nameHigh

Fix for missing resource requests/limits:

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    memory: 256Mi  # omit CPU limit to avoid throttling

Run and include output:

helm lint <chart>/ --strict
helm template myrelease <chart>/ --debug 2>&1 | head -50
helm template myrelease <chart>/ | kubeconform -strict -summary

Output format:

HELM CHART REVIEW — <chart name>

CRITICAL: <count>
HIGH:     <count>
MEDIUM:   <count>
LOW:      <count>

[CRITICAL] Missing resource limits on container <name>
  Fix: add resources.requests and resources.limits to deployment.yaml
  ...

Mode: security

Audit using these tables:

Pod Security

CheckSeverityFix
No pod securityContextCriticalAdd runAsNonRoot: true, runAsUser: 1000, fsGroup: 1000, seccompProfile.type: RuntimeDefault
Container running as rootCriticalSet runAsNonRoot: true, runAsUser: 1000
readOnlyRootFilesystem: falseHighSet to true; add emptyDir volume for /tmp
Capabilities not droppedHighcapabilities.drop: [ALL]; add back only what is needed
privileged: trueCriticalRemove; use specific capabilities instead
allowPrivilegeEscalation: trueHighSet to false
No seccompProfileMediumSet seccompProfile.type: RuntimeDefault

RBAC

CheckSeverityFix
No dedicated ServiceAccountMediumCreate one; do not use default SA
automountServiceAccountToken: trueMediumSet to false unless pod needs K8s API access
ClusterRole instead of RoleMediumUse namespace-scoped Role unless cluster-wide is justified
Wildcard verbs or resourcesCriticalUse explicit verbs and resource names

Network and Secrets

CheckSeverityFix
No NetworkPolicyMediumAdd default-deny ingress + explicit allow
Secrets in values.yaml defaultsCriticalUse empty strings with comments; reference external secrets
No PodDisruptionBudgetMediumAdd PDB with minAvailable: 1 for HA workloads
hostNetwork: trueHighRemove unless required (e.g., CNI plugin)
hostPID: true or hostIPC: trueCriticalNever in application charts
Image tag latest or mutableHighPin to a digest or explicit version

Output format:

SECURITY AUDIT — <chart name>

CRITICAL: <count>
HIGH:     <count>
MEDIUM:   <count>
LOW:      <count>

[CRITICAL] <finding>: exact problem
  Fix: corrected YAML snippet

Mode: upgrade

Verify a Helm upgrade is safe before applying it.

Step 1 — Check helm-diff is installed

helm plugin list | grep diff || helm plugin install https://github.com/databus23/helm-diff

Step 2 — Render diff

helm diff upgrade <release> <chart>/ \
  --values values.yaml \
  --allow-unreleased

Step 3 — Breaking change checks

Scan the diff output for:

PatternRiskAction
selector: changed on Deployment/StatefulSetCritical — will failRename release or delete + recreate
storageClassName changed on PVCCriticalManual migration required
kind: changed (e.g. Deployment → StatefulSet)CriticalDelete old resource first
ServiceAccount name changedHighOld RBAC bindings become orphaned
ConfigMap or Secret removedHighVerify no pods still reference them
Resource limits decreased >50%MediumRisk of OOMKill on rollout
Replica count → 1MediumHA gap during rollout

Step 4 — Dry run

helm upgrade --dry-run <release> <chart>/ --values values.yaml

Step 5 — Rollback plan

Always print:

# Rollback to previous revision if upgrade fails
helm rollback <release>     # omit revision to roll back to the previous release
helm status <release>

Mode: schema

Generate a values.schema.json from an existing values.yaml.

Read the values.yaml and produce a JSON Schema draft-07 document that:

  • Infers type from the YAML value (string, integer, boolean, object, array)
  • Sets description from inline comments (if present)
  • Marks keys without a safe default as required
  • Validates enum fields where the comment lists allowed values (e.g. # one of: debug, info, warn, error)
  • Uses $defs for repeated shapes

Write the file as <chart>/values.schema.json.

Validate it immediately:

helm lint <chart>/ --strict
# helm lint runs schema validation automatically — any mismatch is reported

Mode: test

Scaffold or run Helm test hooks.

If no test files exist — scaffold a test hook. Pattern depends on workload type:

Service-based charts (web service, stateful) — HTTP connectivity test:

apiVersion: v1
kind: Pod
metadata:
  name: "{{ include "<chart>.fullname" . }}-test-connection"
  labels:
    {{- include "<chart>.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": test
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  restartPolicy: Never
  containers:
    - name: wget
      image: busybox:1.36
      command: ['wget']
      args: ['{{ include "<chart>.fullname" . }}:{{ .Values.service.port }}{{ .Values.healthCheck.path | default "/healthz" }}']

Worker / CronJob charts (no Service) — verify the pod ran without error:

apiVersion: v1
kind: Pod
metadata:
  name: "{{ include "<chart>.fullname" . }}-test-run"
  labels:
    {{- include "<chart>.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": test
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  restartPolicy: Never
  containers:
    - name: check
      image: busybox:1.36
      command: ['sh', '-c', 'echo "test: worker chart deployed successfully"']

If test files exist — run them:

# Install the chart first if not already installed
helm install myrelease <chart>/ --wait

# Run tests
helm test <release>

# Show logs on failure
helm test <release> --logs

Mode: deps

Manage chart dependencies declared in Chart.yaml.

List declared dependencies:

helm dependency list <chart>/

Add a dependency — ask:

Dependency name? (e.g. postgresql, redis)
Repository URL? (e.g. https://charts.bitnami.com/bitnami)
Version constraint? (e.g. 13.x.x)
Alias? (optional — useful if adding same chart twice)

Then add to Chart.yaml under dependencies: and run:

helm dependency update <chart>/
helm dependency build <chart>/

Verify integrity:

helm dependency list <chart>/
# All entries should show "ok" in the Status column

Update all to latest matching constraint:

helm dependency update <chart>/

After any dependency change, re-run lint:

helm lint <chart>/ --strict

Closing — Log learnings

After completing any mode, log non-obvious fixes or patterns:

  • Unexpected breaking change found during upgrade → log as ERR in .learnings/ERRORS.md
  • Reusable pattern that saved time → log as LRN in .learnings/LEARNINGS.md

Use /platform-skills:self-improve log for each entry worth keeping.

BEFORE_AFTER.md

CHANGELOG.md

CODE_OF_CONDUCT.md

COMMANDS.md

CONTRIBUTING.md

EDITOR_INTEGRATIONS.md

GETTING_STARTED.md

HOW_IT_WORKS.md

install.sh

INSTALLATION.md

LAUNCH.md

PROMPTS.md

QUICKSTART.md

README.md

renovate.json

SECURITY.md

SKILL.md

tessl.json

tile.json