← All skills
helm-chart-builder
Use when creating or reviewing Helm charts, designing values.yaml, writing template helpers, auditing chart security (RBAC, network policies, pod security), or managing subchart dependencies.
About this skill
Use when creating or reviewing Helm charts, designing values.yaml, writing template helpers, auditing chart security (RBAC, network policies, pod security), or managing subchart dependencies.
SKILL.md
helm-chart-builder.SKILL.mdPlace it in your Syntic skills folder. For Syntic Code, install in your local skills directory, review the contents and run it in a controlled environment first.
---
name: helm-chart-builder
description: Use when creating or reviewing Helm charts, designing values.yaml, writing template helpers, auditing chart security (RBAC, network policies, pod security), or managing subchart dependencies.
category: Engineering
version: 1.0.0
tools: []
---
# Helm Chart Builder
Guide production-grade Helm chart design: chart scaffolding, values.yaml design, template patterns, dependency management, and security hardening. Opinionated decisions that turn ad-hoc Kubernetes manifests into maintainable, testable, reusable charts — not a Helm tutorial.
## When to apply
Recognize these requests: creating a Helm chart for a service, reviewing an existing chart, auditing chart security, designing a values.yaml, adding a subchart dependency, setting up helm tests, or asking for best practices for a workload type. Any mention of Helm chart, values.yaml, Chart.yaml, templates, _helpers.tpl, subcharts, helm lint, or helm test applies.
## Chart scaffolding
Identify the workload type first, since it determines the resource set:
- Web service → Deployment + Service + Ingress
- Worker → Deployment, no Service
- CronJob → CronJob + ServiceAccount
- Stateful service → StatefulSet + PVC + Headless Service
- Library chart → no templates, only shared helpers
Describe the standard chart layout for the user to create: `Chart.yaml` (metadata + dependencies), `values.yaml` (default configuration), an optional `values.schema.json` for validation, `.helmignore`, and a `templates/` directory containing `_helpers.tpl` (named templates/helpers), the workload resource, `service.yaml`, `ingress.yaml` if applicable, `serviceaccount.yaml`, `hpa.yaml`, `pdb.yaml`, `networkpolicy.yaml`, `configmap.yaml`/`secret.yaml` as needed, `NOTES.txt` for post-install instructions, a `tests/` subfolder with a connection test, plus a `charts/` directory for subchart dependencies.
**Chart.yaml conventions:**
- `apiVersion: v2` only (Helm 3) — never v1
- `name` matches the directory name exactly
- `version` is the chart's own semver, distinct from `appVersion` (the packaged application's version string)
- `description` is a one-line summary
- `type: application` (or `library` for shared-helper-only charts)
- Dependencies: pin versions with `~X.Y.Z` (patch-level float), use `condition` to make subcharts optional, use `alias` for multiple instances of the same subchart, and remind the user to run `helm dependency update` after any change
**values.yaml conventions:** every value gets an inline comment explaining purpose and type; defaults should work out of the box for development; prefer a flat, override-friendly structure over deep nesting; never hardcode cluster-specific values like image registry, domain, or storage class.
Recommend validation with `helm lint` and `helm template --debug` before the chart ships.
## Chart review checklist
**Structure issues:**
| Check | Severity | Fix |
|---|---|---|
| Missing `_helpers.tpl` | High | Create helpers for common labels and selectors |
| No `NOTES.txt` | Medium | Add post-install instructions |
| No `.helmignore` | Low | Exclude .git, CI files, tests |
| Missing Chart.yaml fields | Medium | Add description, appVersion, maintainers |
| Hardcoded values in templates | High | Extract to values.yaml with defaults |
**Template quality issues:**
| Check | Severity | Fix |
|---|---|---|
| Missing standard labels | High | Use `app.kubernetes.io/*` labels via `_helpers.tpl` |
| No resource requests/limits | Critical | Add a `resources` section with defaults |
| Hardcoded image tag | High | Use `{{ .Values.image.repository }}:{{ .Values.image.tag }}` |
| No `imagePullPolicy` | Medium | Default to `IfNotPresent`, overridable |
| Missing liveness/readiness probes | High | Add probes with configurable paths/ports |
| No pod anti-affinity | Medium | Add preferred anti-affinity for HA |
| Duplicate template code | Medium | Extract into named templates in `_helpers.tpl` |
Summarize findings by severity count (Critical/High/Medium/Low) with fix recommendations per finding.
## Security audit checklist
**Pod security:**
| Check | Severity | Fix |
|---|---|---|
| No `securityContext` | Critical | Add `runAsNonRoot`, `readOnlyRootFilesystem` |
| Running as root | Critical | Set `runAsNonRoot: true`, `runAsUser: 1000` |
| Writable root filesystem | High | `readOnlyRootFilesystem: true` + emptyDir for /tmp |
| All capabilities retained | High | Drop ALL, add only specifically needed caps |
| Privileged container | Critical | `privileged: false`, use specific capabilities |
| No seccomp profile | Medium | `seccompProfile.type: RuntimeDefault` |
| `allowPrivilegeEscalation: true` | High | Set to `false` |
**RBAC:**
| Check | Severity | Fix |
|---|---|---|
| No ServiceAccount | Medium | Create a dedicated SA, don't use default |
| `automountServiceAccountToken: true` | Medium | Set `false` unless the pod needs K8s API access |
| ClusterRole instead of Role | Medium | Use namespace-scoped Role unless cluster-wide is required |
| Wildcard permissions | Critical | Use specific resource names and verbs |
| No RBAC at all | Low | Acceptable if the pod doesn't need K8s API access |
**Network and secrets:**
| Check | Severity | Fix |
|---|---|---|
| No NetworkPolicy | Medium | Default-deny ingress + explicit allow rules |
| Secrets in values.yaml | Critical | Use an external-secrets operator or sealed-secrets |
| No PodDisruptionBudget | Medium | Add PDB with `minAvailable` for HA workloads |
| `hostNetwork: true` | High | Remove unless absolutely required (e.g. CNI plugin) |
| `hostPID`/`hostIPC` | Critical | Never use in application charts |
Report findings the same way: severity counts plus remediation steps per finding.
## Template patterns worth knowing
- **Standard labels helper** — a `_helpers.tpl` named template defining common labels (`helm.sh/chart`, `app.kubernetes.io/name`, `app.kubernetes.io/instance`, `app.kubernetes.io/version`, `app.kubernetes.io/managed-by`) plus a narrower `selectorLabels` template (name + instance only, since selectors must be immutable across upgrades).
- **Conditional resources** — wrap optional resources (e.g. Ingress) in `{{- if .Values.x.enabled -}}`, and iterate `tls`/`hosts`/`paths` values with `range` so the chart adapts to however many hosts or paths are configured.
- **Security-hardened pod spec** — a dedicated ServiceAccount with `automountServiceAccountToken: false`, pod-level `securityContext` (`runAsNonRoot`, fixed `runAsUser`/`fsGroup`, `seccompProfile: RuntimeDefault`), container-level `securityContext` (`allowPrivilegeEscalation: false`, `readOnlyRootFilesystem: true`, capabilities dropped to `ALL`), image reference built from `.Values.image.repository`/`.tag`, resources sourced from `.Values.resources`, and an `emptyDir` mounted at `/tmp` to compensate for the read-only root filesystem.
## Values design principles
- **Structure:** flat over nested (`image.tag`, not `container.spec.image.tag`); group by resource (`service.*`, `ingress.*`, `resources.*`); use `enabled: true/false` for optional resources; document every key inline; ship sensible development defaults.
- **Naming:** camelCase keys (`replicaCount`, not `replica_count`); boolean keys as adjectives (`enabled`, `required`) not verbs; nested keys no more than 3 levels deep; match upstream conventions (`image.repository`, `image.tag`, `image.pullPolicy`).
- **Anti-patterns:** hardcoded cluster URLs/domains, secrets as default values, empty strings where `null` is correct, structures nested past 3 levels, undocumented values, and a values.yaml that doesn't work without overrides.
## Dependency management
Subcharts: declare them under Chart.yaml `dependencies` (Helm 3 — not the old `requirements.yaml`), pin versions like `~15.x.x`, use `condition:` to make a subchart optional (e.g. `condition: postgresql.enabled`), use `alias:` for multiple instances of the same chart, override subchart values under the subchart's name key in values.yaml, and remind the user to run `helm dependency update` before packaging.
Library charts: `type: library` in Chart.yaml, no `templates/` output — only exported named templates, no rendered resources — used for shared labels, annotations, and security contexts, versioned independently from the application charts that consume them.
## Flag proactively, without being asked
- No `_helpers.tpl` → every chart needs standard labels and a fullname helper
- Hardcoded image tag in a template → must be overridable via values.yaml
- No resource requests/limits → pods without limits can starve the node
- Running as root → add a securityContext; no exceptions for production charts
- No `NOTES.txt` → users need post-install instructions
- Secrets in values.yaml defaults → replace with placeholders and comments explaining how to supply real secrets
- No liveness/readiness probes → Kubernetes needs to know if the pod is healthy
- Missing `app.kubernetes.io/*` labels → required for proper resource tracking
Install
Syntic App
- 1Create a dedicated folder for this skill in your local skills library.
- 2Place SKILL.md into that folder.
- 3Restart Syntic and invoke this skill on matching tasks.
Syntic Code (CLI)
- 1Save SKILL.md in your local Syntic Code skills directory.
- 2Keep related files in the same skill folder.
- 3Run in a safe environment and validate outputs.