Helm is the standard package manager for Kubernetes, making application deployments reproducible and manageable. However, chart quality directly impacts operational costs. Over-abstracted templates, insufficient testing, and overlooked security gaps can cause serious issues in production. Kubo adopts Helm charts as the standard pattern for declarative deployment, and the best practices in this article apply directly to Kubo environments.
Chart Structure and Directory Design
Helm chart quality is determined at the structural design stage. Here are best practices derived from the Helm official documentation and real-world experience.
Standard Chart Structure
my-app/
├── Chart.yaml # Chart metadata
├── Chart.lock # Dependency lock file
├── values.yaml # Default configuration values
├── values-dev.yaml # Development environment overrides
├── values-staging.yaml # Staging environment overrides
├── values-production.yaml # Production environment overrides
├── templates/
│ ├── _helpers.tpl # Common template helpers
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ ├── configmap.yaml
│ └── tests/
│ └── test-connection.yaml
├── charts/ # Subcharts (dependencies)
└── .helmignore
Separating Environment-Specific Values Files
As Carlos Neto's blog points out, trying to manage all environments in a single values.yaml leads to bloated, unmanageable files. Separate values files per environment and specify them explicitly at deploy time.
# Development environment
helm upgrade --install my-app ./my-app -f values.yaml -f values-dev.yaml
# Production environment
helm upgrade --install my-app ./my-app -f values.yaml -f values-production.yaml
Define defaults in values.yaml and include only the values that need overriding in environment-specific files. This keeps each file small and makes differences clear.
Template Design Principles: DRY and YAGNI
Two critical principles determine Helm template quality.
DRY (Don't Repeat Yourself)
Manifest duplication is a breeding ground for errors. Extract common patterns into _helpers.tpl and reuse them as template functions.
{{/* _helpers.tpl */}}
{{- define "my-app.labels" -}}
app.kubernetes.io/name: {{ include "my-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "my-app.chart" . }}
{{- end }}
{{- define "my-app.selectorLabels" -}}
app.kubernetes.io/name: {{ include "my-app.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
YAGNI (You Aren't Gonna Need It)
Over-abstraction of templates is the biggest anti-pattern. The practical Helm development guide warns that "every extra function, conditional statement, or parameter adds cognitive overhead."
Bad example: Unnecessary abstraction
{{- if .Values.deployment.enabled }}
{{- if .Values.deployment.strategy.enabled }}
strategy:
type: {{ .Values.deployment.strategy.type | default "RollingUpdate" }}
{{- end }}
{{- end }}
Good example: Simple and sufficient
strategy:
type: {{ .Values.strategy | default "RollingUpdate" }}
Write simple templates that meet current requirements. Don't add complexity for hypothetical future scenarios.
Leveraging Library Charts
For organizations with multiple charts, consolidate common templates into a library chart like the Bitnami Common Chart. Library charts don't deploy resources directly---they only provide template helpers.
# Chart.yaml
dependencies:
- name: common
version: 2.x.x
repository: https://charts.bitnami.com/bitnami
Captain.AI can assist with AI-powered template quality analysis and optimization recommendations.
Testing Strategy: Multi-Layer Quality Assurance
Helm chart testing directly impacts ci-cd pipeline reliability. Combine the Chart Testing (ct) tool and helm-unittest for a multi-layered testing approach.
Layer 1: Lint and Static Validation
# Helm's built-in lint
helm lint ./my-app --values values-production.yaml
# Strict linting with Chart Testing
ct lint --chart-dirs charts/ --all
Layer 2: Unit Tests (helm-unittest)
# tests/deployment_test.yaml
suite: Deployment Tests
templates:
- deployment.yaml
tests:
- it: should set correct replicas
set:
replicaCount: 5
asserts:
- equal:
path: spec.replicas
value: 5
- it: should have resource limits
asserts:
- isNotEmpty:
path: spec.template.spec.containers[0].resources.limits
- it: should use correct image
set:
image.repository: my-registry/my-app
image.tag: "v1.0.0"
asserts:
- equal:
path: spec.template.spec.containers[0].image
value: "my-registry/my-app:v1.0.0"
Layer 3: Template Rendering Validation
# Render templates to YAML and validate
helm template my-app ./my-app -f values-production.yaml | kubeval --strict
Layer 4: Integration Tests (helm test)
Use Helm's chart test feature to run post-deployment tests against the live environment.
# templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "my-app.fullname" . }}-test"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: curl-test
image: curlimages/curl:latest
command: ['curl', '--fail', 'http://{{ include "my-app.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
helm test my-release
OCI Registries and Chart Distribution
Since Helm 3.8, OCI (Open Container Initiative) registries have been the recommended mechanism for chart distribution. Managing charts in the same registry as container images enables unified operations.
Pushing to an OCI Registry
# Package the chart
helm package ./my-app
# Log in to the registry
helm registry login ghcr.io -u $GITHUB_USER -p $GITHUB_TOKEN
# Push
helm push my-app-1.0.0.tgz oci://ghcr.io/your-org/charts
Installing from an OCI Registry
helm install my-app oci://ghcr.io/your-org/charts/my-app --version 1.0.0
Automated Publishing in ci-cd Pipelines
# GitHub Actions example
- name: Publish Helm Chart
run: |
helm package ./charts/my-app --version ${{ github.ref_name }}
helm push my-app-${{ github.ref_name }}.tgz \
oci://ghcr.io/${{ github.repository_owner }}/charts
Kubo integrates an OCI-compliant registry, making chart publishing and management seamless.
Security and Resource Management
Proper Resource Limits
# values.yaml
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Adjust resource values per environment. Keep development minimal; set appropriate values for production traffic.
Security Context
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
Externalizing Secrets
Reference Kubernetes Secrets within templates while managing the actual secret data through External Secrets Operator or Sealed Secrets.
Vulnerability Scanning
# Scan charts and referenced images with Trivy
trivy config ./my-app/
trivy image my-registry/my-app:v1.0.0
# Configuration scanning with Kubescape
kubescape scan framework nsa ./my-app/
Integrate Trivy and Kubescape into your CI pipeline to detect security issues before deployment.
Conclusion: Maximize Helm Charts with Kubo
Helm chart quality determines the success of Kubernetes operations. By balancing DRY and YAGNI, implementing multi-layer testing, distributing via OCI registries, and following security best practices, you can develop charts that are maintainable and secure.
Kubo supports declarative deployment via Helm charts as a standard pattern, with built-in integration for ArgoCD and FluxCD. Captain.AI provides AI-powered chart quality analysis and template optimization, maximizing developer productivity. Ready to improve your Helm chart development? Contact us today.