Helm bjw s chart
Generate production-ready Helm charts using the bjw-s-labs common library (app-template v5, with v4 legacy support). Use when creating a new Helm chart, converting Docker Compose to Helm, configuring controllers with sidecars or init containers, setting up services/ingress/persistence, HorizontalPodAutoscalers, ServiceMonitors/PodMonitors, NetworkPolicies, or handling StatefulSets and multi-controller deployments.From its SKILL.md
npx -y skills add obeone/claude-skills --skill helm-bjw-s-chartAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 8 commands, including `helm dependency update` and 7 more.
SKILL.md
13.5 KB, ~3.4k tokens by cl100k_base, as published. Nobody here has run it
Helm bjw-s Chart Generator
π v5.0.0 β common 5.x is now the default β The skill is rebased on
common: 5.0.1(released 2026-05-14). New charts target 5.x out of the box, and the migration path from 4.x is captured inreferences/migration-4-to-5.md. 4.x remains supported as a legacy track for clusters that can't meet the Kubernetes 1.31 / Helm 3.18 prerequisites.π Renamed in v3.0.0 β This skill was previously published as
helm-chart-generator. Update any agent, automation, or documentation that still references the old name.
Generate production-ready Helm charts using the bjw-s-labs common library (app-template v5, with v4 legacy support).
Library version matrix
| common | Kubernetes | Helm | Status |
|---|---|---|---|
5.0.1 | >= 1.31 | >= 3.18 | Default β latest stable, all examples target it |
4.6.2 | >= 1.25 | >= 3.14 | Legacy β pin when the cluster can't meet 5.x reqs |
Everything documented here works on common 5.x by default. When a
pattern is not available on 4.x it's tagged (5.x only) so
agents pinned to the legacy track can skip it. See
references/migration-4-to-5.md for
the full 4 β 5 upgrade procedure.
Migration 4.x β 5.x at a glance
Five things to know β full details in
references/migration-4-to-5.md:
automountServiceAccountToken: falseis now the default. Flip it back totrueper-pod if the workload needs to call the Kubernetes API.- A default unprivileged ServiceAccount is created for every release.
Opt out with
global.createDefaultServiceAccount: falsewhen you reference an externally-managed SA. rawResourceswas restructured β manifest content moved out ofspec:into amanifest:wrapper, and labels/annotations now live undermetadata:. Only relevant if you userawResources(rare).- ServiceMonitor / PodMonitor
jobLabeldefaults toapp.kubernetes.io/name. Override if your Prometheus rules depended on the oldmetadata.namedefault. - Minimums bumped: Kubernetes β₯ 1.31, Helm β₯ 3.18.
Quick Start Workflow
-
Understand the application
- Ask about the container image, ports, environment variables
- Identify if sidecars/init containers are needed
- Determine storage requirements (config, data, logs)
- Check if ingress/networking is required
-
Generate base structure
- Use templates from
assets/templates/ - Start with Chart.yaml and basic values.yaml
- Add templates/common.yaml (minimal, just includes library)
- Create templates/NOTES.txt for post-install instructions
- Use templates from
-
Build values.yaml progressively
- Controllers and containers (main app + sidecars if needed)
- Services (one per controller or port)
- Ingress if web-accessible
- Persistence for stateful data
- Secrets/ConfigMaps if needed
-
Validate and refine
- Run
helm dependency updateto fetch dependencies (generates Chart.lock) - Use
scripts/validate_chart.pyto check structure - Review against
references/best-practices.md - Test with
helm templateandhelm lint
- Run
Core Structure
Every chart consists of:
my-app/
βββ Chart.yaml # Chart metadata and dependencies
βββ values.yaml # Configuration values
βββ templates/
βββ common.yaml # Includes the bjw-s library
βββ NOTES.txt # Post-install instructions
Chart.yaml Template
apiVersion: v2
name: <app-name>
description: <brief description>
type: application
version: 1.0.0
appVersion: "<app version>"
dependencies:
- name: common
repository: https://bjw-s-labs.github.io/helm-charts
version: 5.0.1 # Default. Pin to 4.6.2 for legacy clusters (K8s < 1.31 / Helm < 3.18).
templates/common.yaml (Always the same)
{{- include "bjw-s.common.loader.all" . }}
templates/NOTES.txt
Provide useful post-install information:
- How to access the application
- Default credentials if any
- Next steps for configuration
values.yaml Structure
Follow this order for clarity:
# 1. Default Pod options (optional)
defaultPodOptions:
# 5.x default is false; set to true only if the pod calls the K8s API.
automountServiceAccountToken: false
securityContext: {}
annotations: {}
# 2. Controllers (required)
controllers:
main: # or custom name
containers:
main: # or custom name
image: {}
env: {}
probes: {}
# 3. Service (required if exposing)
service:
main:
controller: main
ports: {}
# 4. Ingress (optional)
ingress:
main:
className: ""
hosts: []
# 5. Persistence (optional)
persistence:
config:
type: persistentVolumeClaim
# or: emptyDir, configMap, secret, nfs, hostPath, ephemeral
# 6. ConfigMaps/Secrets (optional)
configMaps: {}
secrets: {}
Common Patterns
Single Container Application
controllers:
main:
containers:
main:
image:
repository: nginx
tag: "1.25-alpine"
pullPolicy: IfNotPresent
service:
main:
controller: main
ports:
http:
port: 80
persistence:
config:
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 1Gi
globalMounts:
- path: /config
Application with Sidecar
controllers:
main:
containers:
main:
image:
repository: myapp
tag: "1.0.0"
sidecar:
dependsOn: main
image:
repository: sidecar-image
tag: "1.0.0"
See references/patterns.md for more examples:
- Multi-controller setups
- Init containers
- VPN sidecars (gluetun)
- Code-server sidecars
- Shared volumes between containers
- Private registries with
imagePullSecrets - StatefulSets with headless service
- HorizontalPodAutoscaler (5.x only)
- PodMonitor (scrape pods without a Service) (5.x only)
- Generic ephemeral volumes (5.x only)
- NetworkPolicy with single-controller auto-targeting (5.x only)
5.x-only features
The patterns below require common >= 5.0.0. They are silently ignored
or rejected on 4.x.
# 1. HorizontalPodAutoscaler tied to a controller
horizontalPodAutoscaler:
main:
enabled: true
controller: main # Target controller identifier
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
# 2. PodMonitor (alternative to ServiceMonitor β scrapes pods directly)
podMonitor:
main:
enabled: true
controller: main
endpoints:
- port: metrics
path: /metrics
interval: 30s
# 3. Generic ephemeral volumes (per-pod PVC, deleted with the pod)
persistence:
scratch:
type: ephemeral
accessMode: ReadWriteOnce
size: 5Gi
storageClass: fast-ssd
globalMounts:
- path: /scratch
# 4. Container / Pod resizePolicy (in-place vertical scaling)
# container resizePolicy: Kubernetes >= 1.35
# pod resizePolicy: Kubernetes >= 1.36
controllers:
main:
pod:
resizePolicy: PreferNoRestart # k8s >= 1.36
containers:
main:
resizePolicy: # k8s >= 1.35
- resourceName: cpu
restartPolicy: NotRequired
- resourceName: memory
restartPolicy: RestartContainer
# 5. NetworkPolicy auto-targets the only controller when neither
# `controller` nor `podSelector` is given. `controller` and
# `podSelector` are now mutually exclusive.
networkpolicies:
default-deny:
enabled: true
policyTypes:
- Egress
Best Practices
Always:
- Use specific image tags, never
:latest - Set resource limits and requests
- Configure health checks (liveness, readiness, startup)
- Use non-root security contexts when possible
- Reference services by identifier, not name
Naming:
- Controllers: Use descriptive names (not just "main")
- Containers: Use descriptive names (not just "main")
- Services: Match controller name or purpose
Security:
- Keep
automountServiceAccountToken: false(5.x default) unless the workload calls the K8s API β and even then, pair it with an explicit ServiceAccount + RBAC, not the auto-created default - Configure proper
securityContext - Use secrets for sensitive data
- Use
imagePullSecretsfor private registries (seereferences/patterns.md)
Persistence:
- Use
globalMountsfor simple cases - Use
advancedMountsfor complex multi-container scenarios - Specify
existingClaimfor pre-created PVCs - Use
type: ephemeralfor scratch space tied to the pod lifecycle (5.x only)
See references/best-practices.md for
comprehensive guidelines.
Validation
After generating a chart:
# 1. Fetch dependencies (required before helm commands)
cd /path/to/chart
helm dependency update
# 2. Validate structure
python scripts/validate_chart.py /path/to/chart
# Or with JSON output for CI:
python scripts/validate_chart.py --json /path/to/chart
# 3. Helm validation
helm lint .
helm template . --debug
# 4. Dry-run installation
helm install --dry-run --debug my-release .
The validator warns when the chart still pins common 4.x, when
rawResources uses the legacy spec: shape (removed in 5.x), when
an external ServiceAccount is referenced without
global.createDefaultServiceAccount: false, when Chart.lock is
missing, or when a declared dependency has no vendored tarball under
charts/.
Pre-Deploy Checklist
Before deploying to a cluster, verify:
- All image tags are pinned (no
:latest) - Resources (requests + memory limits) are set on every container
- Health probes configured (liveness + readiness minimum)
-
securityContextset: non-root,readOnlyRootFilesystem, drop ALL capabilities -
automountServiceAccountToken: falseunless explicitly needed - If using an external ServiceAccount,
global.createDefaultServiceAccount: falseis set - If
rawResourcesis in play, manifest uses the 5.xmanifest:wrapper (not legacyspec:) - Secrets reference external sources, not hardcoded values
-
helm dependency updaterun, withChart.lockand the populatedcharts/published (see Publishing the Chart) -
helm lintpasses with no errors
Publishing the Chart
A published chart must be self-contained. Both of these have to ship
inside the packaged .tgz (and the committed chart):
Chart.lockβ pins the exact resolved dependency versions and digests.- The full
charts/directory β the vendored dependency tarballs (common-<version>.tgz, β¦) materialized byhelm dependency update.
helm package bundles whatever is under charts/ at package time, and the
default .helmignore excludes neither Chart.lock nor charts/. The rule
is therefore about making sure both are present before you package:
# Vendor dependencies into charts/ and (re)generate Chart.lock
helm dependency update
# ...then package β the .tgz now embeds charts/common-<version>.tgz
helm package .
Two equally valid ways to satisfy it:
- Commit
charts/to git (vendoring): the checkout is already self-contained; the publish step is justhelm package. - Gitignore
charts/but commitChart.lock, then runhelm dependency buildin the publish pipeline beforehelm package.helm dependency buildrepopulatescharts/from the locked versions without re-resolving them.
Never publish a chart whose charts/ is empty while Chart.yaml declares
dependencies: consumers without the bjw-s-labs.github.io/helm-charts repo
pre-added (offline / air-gapped installs) cannot resolve the common library
at install time. scripts/validate_chart.py warns when Chart.lock is
missing or when a declared dependency has no matching tarball under
charts/.
Common Issues
Services not found: Use identifier not name in ingress paths
Mounts not working: Check globalMounts vs advancedMounts usage
Names too long: Use nameOverride or fullnameOverride in global settings
Controller not starting: Check dependsOn order for init/sidecar containers
Unexpected ServiceAccount appears (5.x): Set global.createDefaultServiceAccount: false or define your own SA
Pod can't talk to the K8s API (5.x): Set automountServiceAccountToken: true on the pod AND grant RBAC
References
references/migration-4-to-5.md- Full 4 β 5 upgrade procedurereferences/patterns.md- Common deployment patternsreferences/best-practices.md- Kubernetes/Helm best practicesreferences/values-schema.md- Complete values.yaml referenceassets/templates/- Base templates for quick start
What ships with it: 11 files
88.0 KB alongside SKILL.md, 1 of them executable
assets/
references/
- best-practices.md14.7 KB
- migration-4-to-5.md8.6 KB
- patterns.md16.0 KB
- values-schema.md13.7 KB
scripts/
- requirements.txt12 B
- validate_chart.pyruns25.1 KB
- README.md6.3 KB