helm-best-practices
Original:🇺🇸 English
Translated
Expert guidance for authoring and maintaining Helm charts following standardized conventions, global registry support, templating best practices, and Kubernetes deployment patterns.
1installs
Sourcecosmonic-labs/skills
Added on
NPX Install
npx skill4agent add cosmonic-labs/skills helm-best-practicesTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Helm Chart Style Guide
This skill provides standardized conventions for authoring and maintaining Helm charts, with a focus on:
- Global registry override using
.Values.global.image.registry - Clear, minimal templating
- Consistent blocks for all containers
image:
When to Use
Activate this skill when:
- Creating new Helm charts
- Reviewing or modifying existing Helm charts
- Configuring image registries for air-gapped environments
- Setting up multi-chart deployments with Helmfile
Image Configuration Best Practices
All charts must support the top-level configuration for global image settings.
yaml
global:
image:
registry: registry.mycompany.comThis enables centralized control of image sources across all dependencies and microservices.
Consistent Image Blocks
All charts should follow a consistent block for every containerized application.
image:Fields should be templated for , , , , and for all containers.
registryrepositorytagpullPolicypullSecretsEvery chart must define all image values with reasonable defaults in :
values.yamlyaml
prometheus:
image:
registry: docker.io
repository: prom/prometheus
tag: v2.52.0
pullPolicy: IfNotPresentTemplating Pattern for Registry Override
Use a registry value at the top of the template. This pattern ensures ability to use internal registries (e.g., ) for air-gapped environments or mirrored image sources:
registry.mycompany.comgotmpl
{{- $registry := .Values.prometheus.image.registry | default .Values.global.image.registry | default "docker.io" -}}
image:
registry: {{ $registry }}
repository: {{ .Values.prometheus.image.repository }}
tag: {{ .Values.prometheus.image.tag }}
pullPolicy: {{ .Values.prometheus.image.pullPolicy }}Templating Conventions
Template only when necessary. Keep templates readable and manageable by avoiding over-templating.
Template:
- Labels
- Annotations
- Resource requests and limits for CPU and memory for each container
- Service port numbers and names
Avoid Templating:
- Most values already present in unless dynamically constructed
values.yaml
Linting & Validation
- Run before commits
helm lint - Use for rendering checks
helm template - Ensure and
values.yamlare fully in sync with templated expectationsChart.yaml
Automated Chart Testing with ct
Use the chart-testing tool () to automate linting, installation, and upgrade checks for charts.
ctInstalling ct
Install (chart-testing) locally:
ctsh
brew install helm/chart-testing/ct
# or via Docker:
# docker pull quay.io/helmpack/chart-testingUsing ct
sh
ct lint --config charts/your-chart/ct.yaml
ct install --config charts/your-chart/ct.yamlTypical workflow:
- Lint all charts:
ct lint --all - Install and test charts:
ct install --all - Test only changed charts:
ct lint --charts charts/your-chart
Best Practices
- Always run and
ct lintbefore submitting a PRct install - Ensure is up to date with chart locations and test settings
ct.yaml - Integrate into CI pipelines for automated validation
ct - Address all errors and warnings before merging
Helmfile Multi-Chart Management
Helmfile enables declarative management of multiple Helm charts and environments.
Installing Helmfile
sh
brew install helmfile
# or via Docker:
# docker run --rm -v $PWD:/apps -w /apps ghcr.io/helmfile/helmfile:latest helmfile --helpUsing Helmfile
- Define releases and environments in or
helmfile.yamlhelmfile.d/*.yaml - Use blocks to layer configuration and support overrides per environment
values: - Run to validate all releases and values
helmfile lint - Apply changes with (dry-run with
helmfile applyfirst)--dry-run - Sync state with to ensure all releases match the desired state
helmfile sync
Helmfile Best Practices
- Keep environment-specific values in separate files (e.g., ,
values-prod.yaml)values-dev.yaml - Use for sensitive values, leveraging helm-secrets if needed
secrets: - Prefer referencing charts by version for reproducibility
- Use before applying changes to preview impact
helmfile diff - Document all environments and overrides clearly
- Validate with and test deployments in CI where possible
helmfile lint
Naming Conventions
Chart names must be lower case letters and numbers. Words may be separated with dashes (-).
Neither uppercase letters nor underscores can be used in chart names. Dots should not be used in chart names.
YAML files should be indented using two spaces (and never tabs).
CRDs
When working with Custom Resource Definitions (CRDs):
- There is a declaration of a CRD (YAML file with )
kind: CustomResourceDefinition - There are resources that use the CRD (resources with the CRD's and
apiVersion)kind
For a CRD, the declaration must be registered before any resources of that CRD's kind(s) can be used.
With Helm 3, use the special directory in your chart to hold your CRDs. These CRDs are not templated, but will be installed by default when running . If the CRD already exists, it will be skipped with a warning. Use flag to skip CRD installation.
crdshelm install--skip-crdsNote: There is no support for upgrading or deleting CRDs using Helm.
Standard Labels
The following labels are recommended for Helm charts:
| Name | Status | Description |
|---|---|---|
| REC | App name, usually |
| REC | Chart name and version: |
| REC | Always set to |
| REC | Set to |
| OPT | App version: |
| OPT | Component role, e.g., |
| OPT | Top-level application when multiple charts work together |
An item of metadata should be a label if:
- It is used by Kubernetes to identify this resource
- It is useful for operators to query the system
If an item of metadata is not used for querying, it should be set as an annotation instead.
Images
A container image should use a fixed tag or the SHA of the image. Never use , , , or other "floating" tags.
latestheadcanaryPods
All PodTemplate sections should specify a selector:
yaml
selector:
matchLabels:
app.kubernetes.io/name: MyName
template:
metadata:
labels:
app.kubernetes.io/name: MyNameThis makes the relationship between the set and the pod explicit and prevents breaking changes when labels change.
RBAC Configuration
RBAC and ServiceAccount configuration should happen under separate keys:
yaml
rbac:
# Specifies whether RBAC resources should be created
create: true
serviceAccount:
# Specifies whether a ServiceAccount should be created
create: true
# The name of the ServiceAccount to use
# If not set and create is true, a name is generated using the fullname template
name:For complex charts with multiple ServiceAccounts:
yaml
someComponent:
serviceAccount:
create: true
name:
anotherComponent:
serviceAccount:
create: true
name:rbac.createtruefalseServiceAccount Helper Template
yaml
{{/*
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 -}}Templates Directory Structure
The directory should be structured as follows:
templates/- Template files should have the extension if they produce YAML output
.yaml - The extension may be used for template files that produce no formatted content
.tpl - Template file names should use dashed notation (), not camelcase
my-example-configmap.yaml - Each resource definition should be in its own template file
- Template file names should reflect the resource kind (e.g., ,
foo-pod.yaml)bar-svc.yaml
Defined Template Names
All defined template names should be namespaced to avoid collisions with subcharts:
Correct:
yaml
{{- define "nginx.fullname" }}
{{/* ... */}}
{{ end -}}Incorrect:
yaml
{{- define "fullname" -}}
{{/* ... */}}
{{ end -}}Formatting Templates
Templates should be indented using two spaces (never tabs).
Template directives should have whitespace after the opening braces and before the closing braces:
Correct:
text
{{ .foo }}
{{ print "foo" }}
{{- print "bar" -}}Incorrect:
text
{{.foo}}
{{print "foo"}}
{{-print "bar"-}}Checklist for Chart Review
Structure
- Chart name is lowercase with dashes
- YAML files use 2-space indentation
- Templates use extension (or
.yamlfor helpers).tpl - Each resource is in its own template file
Image Configuration
- Supports override
global.image.registry - Uses fixed tags or SHAs, not floating tags
- All image fields are configurable in
values.yaml
Labels & Selectors
- Includes recommended standard labels
- PodTemplate sections have proper selectors
RBAC
- RBAC and ServiceAccount are separate config sections
- defaults to
rbac.createtrue - ServiceAccount helper template is properly namespaced
Validation
- Passes
helm lint - Passes
ct lint - and
values.yamlare in syncChart.yaml