Comprehensive toolkit for validating, linting, testing, and analyzing Helm charts and their rendered Kubernetes resources. Use this skill when working with Helm charts, validating templates, debugging chart issues, working with Custom Resource Definitions (CRDs) that require documentation lookup, or checking Helm best practices.
This skill provides a comprehensive validation and analysis workflow for Helm charts, combining Helm-native linting, template rendering, YAML validation, schema validation, CRD documentation lookup, and security best practices checking.
IMPORTANT: This validator is read-only by default. It analyzes charts and proposes improvements. Only modify files when the user explicitly asks to apply fixes.
Use this skill when one or more of these top cases apply:
Trigger phrase examples:
helm template fail?"Out of scope by default:
helm-generator)bash scripts/setup_tools.sh
bash scripts/validate_chart_structure.sh <chart-directory>
helm lint <chart-directory> --strict
helm template <release-name> <chart-directory> --values <values-file> --debug --output-dir ./rendered
find ./rendered -type f \( -name "*.yaml" -o -name "*.yml" \) -exec yamllint -c assets/.yamllint {} +
find ./rendered -type f \( -name "*.yaml" -o -name "*.yml" \) -exec kubeconform -summary -verbose {} +
Run Mode A plus Stage 8 dry-run commands in this document.
Follow this sequential validation workflow. Each stage catches different types of issues:
Before starting validation, verify required tools are installed:
bash scripts/setup_tools.sh
Required tools:
Fallback policy for unavailable tools or environment constraints:
| Condition | Action | Stage status |
|-----------|--------|--------------|
| helm missing | Run Stage 2 only, then report Stages 3 to 9 as skipped/blocked | ⚠️ Warning |
| yamllint missing | Use yq syntax checks if available; otherwise skip Stage 5 | ⚠️ Warning |
| kubeconform missing | Skip Stage 7 and rely on Stage 6 CRD/manual checks | ⚠️ Warning |
| kubectl missing or no kube-context | Skip Stage 8, continue with remaining stages | ⚠️ Warning |
| No internet access for CRD docs | Use local CRD manifests and kubeconform output, mark doc lookup incomplete | ⚠️ Warning |
If tools are missing, provide installation instructions from scripts/setup_tools.sh output and continue with the fallback path above.
Verify the chart follows the standard Helm directory structure:
bash scripts/validate_chart_structure.sh <chart-directory>
Expected structure:
mychart/
Chart.yaml # Chart metadata (required)
values.yaml # Default values (required)
values.schema.json # JSON Schema for values validation (optional)
templates/ # Template directory (required)
_helpers.tpl # Template helpers (recommended)
NOTES.txt # Post-install notes (recommended)
*.yaml # Kubernetes manifest templates
charts/ # Chart dependencies (optional)
crds/ # Custom Resource Definitions (optional)
.helmignore # Files to ignore during packaging (optional)
Common issues caught:
Run Helm's built-in linter to catch chart-specific issues:
helm lint <chart-directory> --strict
Optional flags:
--values <values-file>: Test with specific values--set key=value: Override specific values--debug: Show detailed error informationCommon issues caught:
Auto-fix approach:
helm lint after fixes are appliedRender templates locally to verify they produce valid YAML:
helm template <release-name> <chart-directory> \
--values <values-file> \
--debug \
--output-dir ./rendered
Options to consider:
--values values.yaml: Use specific values file--set key=value: Override individual values--show-only templates/deployment.yaml: Render specific template--validate: Validate against Kubernetes OpenAPI schema--include-crds: Include CRDs in rendered output--is-upgrade: Simulate upgrade scenario--kube-version 1.28.0: Target specific Kubernetes versionCommon issues caught:
For template errors:
Validate YAML syntax and formatting of rendered templates:
find ./rendered -type f \( -name "*.yaml" -o -name "*.yml" \) \
-exec yamllint -c assets/.yamllint {} +
Common issues caught:
Auto-fix approach:
Before schema validation, detect if the chart contains or renders Custom Resource Definitions:
# Check crds/ directory
if [ -d <chart-directory>/crds ]; then
find <chart-directory>/crds -type f \( -name "*.yaml" -o -name "*.yml" \) \
-exec bash scripts/detect_crd_wrapper.sh {} +
fi
# Check rendered templates
find ./rendered -type f \( -name "*.yaml" -o -name "*.yml" \) \
-exec bash scripts/detect_crd_wrapper.sh {} +
The script outputs JSON with resource information:
[
{
"kind": "Certificate",
"apiVersion": "cert-manager.io/v1",
"group": "cert-manager.io",
"version": "v1",
"isCRD": true,
"name": "example-cert"
}
]
For each detected CRD:
Try context7 MCP first (preferred):
Use mcp__context7__resolve-library-id with the CRD project name
Example: "cert-manager" for cert-manager.io CRDs
"prometheus-operator" for monitoring.coreos.com CRDs
"istio" for networking.istio.io CRDs
Then use mcp__context7__query-docs with:
- libraryId from resolve step
- query: The CRD kind and relevant features (e.g., "Certificate spec required fields")
Fallback to web.search_query (web search) if Context7 fails:
Search query pattern:
"<kind>" "<group>" kubernetes CRD "<version>" documentation spec
Example:
"Certificate" "cert-manager.io" kubernetes CRD "v1" documentation spec
"Prometheus" "monitoring.coreos.com" kubernetes CRD "v1" documentation spec
Extract key information:
specWhy this matters: CRDs have custom schemas not available in standard Kubernetes validation tools. Understanding the CRD's spec requirements prevents validation errors and ensures correct resource configuration.
Validate rendered templates against Kubernetes schemas:
find ./rendered -type f \( -name "*.yaml" -o -name "*.yml" \) -exec \
kubeconform \
-schema-location default \
-schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' \
-summary \
-verbose \
{} +
Options to consider:
-strict to reject unknown fields (recommended for production)-ignore-missing-schemas if working with custom/internal CRDs-kubernetes-version 1.28.0 to validate against specific K8s version-output json for programmatic processingCommon issues caught:
For CRDs: If kubeconform reports "no schema found", this is expected. Use the documentation from Stage 6 to manually validate the spec fields.
Stage 7 success criteria (explicit):
kubeconform exits 0, and no invalid resources are reported.If kubectl is configured and cluster access is available, perform a server-side dry-run:
# Test installation
helm install <release-name> <chart-directory> \
--dry-run=server \
--debug \
--values <values-file>
# Test upgrade
helm upgrade <release-name> <chart-directory> \
--dry-run=server \
--debug \
--values <values-file>
If the Helm version does not support --dry-run=server, use --dry-run and document that only client-side Helm simulation was executed.
This catches:
If dry-run is not possible:
kubectl apply --dry-run=server -f ./rendered/For updates to existing releases:
helm diff upgrade <release-name> <chart-directory>
This shows what would change, helping catch unintended modifications. (Requires helm-diff plugin)
Stage 8 success criteria (explicit):
0 with no admission/policy errors.kubectl/cluster context/access is unavailable, or only client-side fallback was possible.IMPORTANT: This stage is MANDATORY. Analyze rendered templates for security best practices compliance.
Check rendered Deployment/Pod templates for:
Missing securityContext - Look for pods/containers without security settings:
# Check if pod-level securityContext exists
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
Missing container securityContext - Each container should have:
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop:
- ALL
Missing resource limits/requests - Check for:
resources:
limits:
cpu: "100m"
memory: "128Mi"
requests:
cpu: "100m"
memory: "128Mi"
Image tag issues - Flag if using :latest or no tag
Missing probes - Check for liveness/readiness probes
How to check: Read the rendered deployment YAML files and grep for these patterns:
# Check for securityContext
find ./rendered -type f \( -name "*.yaml" -o -name "*.yml" \) \
-exec grep -l "securityContext" {} +
# Check for resources
find ./rendered -type f \( -name "*.yaml" -o -name "*.yml" \) \
-exec grep -l "resources:" {} +
# Check for latest tag
find ./rendered -type f \( -name "*.yaml" -o -name "*.yml" \) \
-exec grep "image:.*:latest" {} +
IMPORTANT: This stage is MANDATORY even if all validations pass. You MUST complete ALL of the following actions.
Default behavior is read-only. Do not modify files unless the user explicitly asks you to apply fixes.
If ANY warnings, errors, or security issues were found, you MUST read:
Read references/helm_best_practices.md
Read references/k8s_best_practices.md
Use these references to provide context and recommendations for each issue found.
Always present a validation summary formatted as a table showing:
Example:
| Stage | Status | Issues |
|-------|--------|--------|
| 1. Tool Check | ✅ Passed | All tools available |
| 2. Structure | ⚠️ Warning | Missing: .helmignore, NOTES.txt |
| 3. Helm Lint | ✅ Passed | 0 errors |
| 4. Template Render | ✅ Passed | 5 templates rendered |
| 5. YAML Syntax | ✅ Passed | No yamllint errors |
| 6. CRD Detection | ✅ Passed | 1 CRD documented |
| 7. Schema Validation | ✅ Passed | All resources valid |
| 8. Dry-Run | ✅ Passed | No cluster errors |
| 9. Security Check | ⚠️ Warning | Missing securityContext |
Group findings by severity:
❌ Errors (must fix):
⚠️ Warnings (should fix):
:latest image tagℹ️ Info (recommendations):
For each issue, provide a proposed fix with:
Example format:
## Proposed Changes
### 1. Add securityContext to Deployment
**File:** templates/deployment.yaml:25
**Severity:** ⚠️ Warning
**Reason:** Running containers as root is a security risk
**Current:**
```yaml
spec:
containers:
- name: app
image: nginx:1.21
Proposed:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: app
image: nginx:1.21
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
File: .helmignore (new file) Severity: ⚠️ Warning Reason: Excludes unnecessary files from chart packaging
Proposed: Copy from assets/.helmignore
#### Step 5: Automation Opportunities
List all detected automation opportunities:
- If `_helpers.tpl` is missing → Recommend: `bash scripts/generate_helpers.sh <chart>`
- If `.helmignore` is missing → Recommend: Copy from `assets/.helmignore`
- If `values.schema.json` is missing → Recommend: Copy and customize from `assets/values.schema.json`
- If `NOTES.txt` is missing → Recommend: Create post-install notes template
- If `README.md` is missing → Recommend: Create chart documentation
#### Step 6: Final Summary
Provide a final summary:
Chart: <chart-name> Status: ⚠️ Warnings Found (or ✅ Ready for Deployment)
Issues Found:
Proposed Changes: N changes recommended
Next Steps:
## Workflow Done Criteria
Validation is complete only when all of the following are true:
- A Stage 1 to Stage 10 status table is present with `✅ Passed`, `⚠️ Warning`, `❌ Failed`, or `⏭️ Skipped` for each stage.
- Every skipped stage includes a concrete tool or environment reason.
- Stage 7 and Stage 8 are evaluated against their explicit success criteria above.
- Severity totals are reported (`Errors`, `Warnings`, `Info`) with proposed remediation actions.
- Role boundary is respected: no file edits unless explicitly requested by the user.
## Helm Templating Automation & Best Practices
This section covers advanced Helm templating techniques, helper functions, and automation strategies.
### Template Helpers (`_helpers.tpl`)
Template helpers are reusable functions defined in `templates/_helpers.tpl`. They promote DRY principles and consistency.
**Standard helper patterns:**
1. **Chart name helper:**
```yaml
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default 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 }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "mychart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- 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 }}
{{/*
Create the name of the service account to use
*/}}
{{- define "mychart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "mychart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
When to create helpers:
Reference and use these Helm template functions for robust charts:
required - Enforce required values:apiVersion: v1
kind: Service
metadata:
name: {{ required "A valid service name is required!" .Values.service.name }}
default - Provide fallback values:replicas: {{ .Values.replicaCount | default 1 }}
quote - Safely quote string values:env:
- name: DATABASE_HOST
value: {{ .Values.database.host | quote }}
include - Use helpers with pipeline:metadata:
labels:
{{- include "mychart.labels" . | nindent 4 }}
tpl - Render strings as templates:{{- tpl .Values.customConfig . }}
toYaml - Convert objects to YAML:{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 2 }}
{{- end }}
fromYaml - Parse YAML strings:{{- $config := .Values.configYaml | fromYaml }}
merge - Merge maps:{{- $merged := merge .Values.override .Values.defaults }}
lookup - Query cluster resources (use carefully):{{- $secret := lookup "v1" "Secret" .Release.Namespace "my-secret" }}
{{- if $secret }}
# Secret exists, use it
{{- else }}
# Create new secret
{{- end }}
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
# ... ingress definition
{{- end }}
{{- range .Values.extraEnvVars }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
{{- range $key, $value := .Values.configMap }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- include "mychart.container" (dict "root" . "container" .Values.mainContainer) }}
Prefer flat structures when possible:
# Good - Flat structure
serverName: nginx
serverPort: 80
# Acceptable - Nested structure for related settings
server:
name: nginx
port: 80
replicas: 3
Always provide defaults in values.yaml:
replicaCount: 1
image:
repository: nginx
pullPolicy: IfNotPresent
tag: "1.21.0"
service:
type: ClusterIP
port: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
Document all values:
# replicaCount is the number of pod replicas for the deployment
replicaCount: 1
# image configures the container image
image:
# image.repository is the container image registry and name
repository: nginx
# image.tag overrides the image tag (default is chart appVersion)
tag: "1.21.0"
Use Helm template comments for documentation:
{{- /*
mychart.fullname generates the fullname for resources.
It supports nameOverride and fullnameOverride values.
Usage: {{ include "mychart.fullname" . }}
*/ -}}
{{- define "mychart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.f
<!-- Content truncated for initial SEO render. Open the source file tab for the full file. -->
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer