Files
2026-03-15 23:28:54 +01:00

9.5 KiB

Values.yaml Design Reference

Design Principles

1. Every Value Is Documented

# Bad — what does this mean?
replicaCount: 1
maxSurge: 25%

# Good — clear purpose, type, and constraints
# -- Number of pod replicas. Ignored when autoscaling.enabled is true.
replicaCount: 1
# -- Maximum number of pods above desired count during rolling update (int or percentage).
maxSurge: 25%

2. Sensible Defaults That Work

A user should be able to helm install mychart . with zero overrides and get a working deployment.

# Bad — broken without override
image:
  repository: ""           # Fails: no image
  tag: ""                  # Fails: no tag

# Good — works out of the box
image:
  repository: nginx        # Default image for development
  tag: ""                  # Defaults to .Chart.AppVersion in template
  pullPolicy: IfNotPresent

3. Flat Over Nested

# Bad — 5 levels deep, painful to override
container:
  spec:
    security:
      context:
        runAsNonRoot: true

# Good — 2 levels, easy to override with --set
securityContext:
  runAsNonRoot: true

Rule of thumb: Max 3 levels of nesting. If you need more, redesign.

4. Group by Resource

# Good — grouped by Kubernetes resource
service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  className: ""
  hosts: []

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 10

Standard Values Structure

# -- Number of pod replicas
replicaCount: 1

# -- Override chart name
nameOverride: ""
# -- Override fully qualified app name
fullnameOverride: ""

image:
  # -- Container image repository
  repository: myapp
  # -- Image pull policy
  pullPolicy: IfNotPresent
  # -- Image tag (defaults to .Chart.AppVersion)
  tag: ""

# -- Image pull secrets for private registries
imagePullSecrets: []

serviceAccount:
  # -- Create a ServiceAccount
  create: true
  # -- Annotations for the ServiceAccount
  annotations: {}
  # -- ServiceAccount name (generated from fullname if not set)
  name: ""
  # -- Automount the service account token
  automount: false

# -- Pod annotations
podAnnotations: {}
# -- Additional pod labels
podLabels: {}

# -- Pod security context
podSecurityContext:
  runAsNonRoot: true
  runAsUser: 1000
  fsGroup: 1000
  seccompProfile:
    type: RuntimeDefault

# -- Container security context
securityContext:
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop:
      - ALL

service:
  # -- Service type
  type: ClusterIP
  # -- Service port
  port: 80

ingress:
  # -- Enable ingress
  enabled: false
  # -- Ingress class name
  className: ""
  # -- Ingress annotations
  annotations: {}
  # -- Ingress hosts
  hosts:
    - host: chart-example.local
      paths:
        - path: /
          pathType: ImplementationSpecific
  # -- Ingress TLS configuration
  tls: []

# -- Container resource requests and limits
resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 500m
    memory: 256Mi

# -- Liveness probe configuration
livenessProbe:
  httpGet:
    path: /healthz
    port: http
  initialDelaySeconds: 15
  periodSeconds: 20

# -- Readiness probe configuration
readinessProbe:
  httpGet:
    path: /readyz
    port: http
  initialDelaySeconds: 5
  periodSeconds: 10

autoscaling:
  # -- Enable horizontal pod autoscaler
  enabled: false
  # -- Minimum replicas
  minReplicas: 1
  # -- Maximum replicas
  maxReplicas: 10
  # -- Target CPU utilization percentage
  targetCPUUtilizationPercentage: 80
  # -- Target memory utilization percentage (optional)
  # targetMemoryUtilizationPercentage: 80

pdb:
  # -- Enable PodDisruptionBudget
  enabled: false
  # -- Minimum available pods
  minAvailable: 1
  # -- Maximum unavailable pods (alternative to minAvailable)
  # maxUnavailable: 1

# -- Node selector constraints
nodeSelector: {}
# -- Tolerations for pod scheduling
tolerations: []
# -- Affinity rules for pod scheduling
affinity: {}

# -- Additional volumes
volumes: []
# -- Additional volume mounts
volumeMounts: []

Anti-Patterns

1. Secrets in Default Values

# BAD — secret visible in chart package, git history, Helm release
database:
  password: "mysecretpassword"
  apiKey: "sk-abc123"

# GOOD — empty defaults with documentation
database:
  # -- Database password (required). Provide via --set or external secret.
  password: ""
  # -- API key. Use external-secrets or sealed-secrets in production.
  apiKey: ""

2. Cluster-Specific Defaults

# BAD — won't work on any other cluster
ingress:
  host: app.my-company.internal
storageClass: gp3
registry: 123456789.dkr.ecr.us-east-1.amazonaws.com

# GOOD — generic defaults
ingress:
  host: chart-example.local
storageClass: ""              # Uses cluster default
image:
  repository: myapp           # Override for private registry

3. Boolean Naming

# BAD — unclear, verb-based
createServiceAccount: true
doAutoScale: false
skipTLS: true

# GOOD — adjective-based, consistent
serviceAccount:
  create: true               # "Is it created?" reads naturally
autoscaling:
  enabled: false             # "Is it enabled?" reads naturally
tls:
  insecureSkipVerify: false  # Matches Go/K8s convention

4. Undocumented Values

# BAD — what are these? What types? What are valid options?
foo: bar
maxRetries: 3
mode: advanced
workers: 4

# GOOD — purpose, type, and constraints are clear
# -- Operation mode. Options: "simple", "advanced", "debug"
mode: advanced
# -- Number of background worker threads (1-16)
workers: 4
# -- Maximum retry attempts for failed API calls
maxRetries: 3

5. Empty String vs Null

# BAD — ambiguous: is empty string intentional?
annotations: ""
nodeSelector: ""

# GOOD — null/empty map means "not set"
annotations: {}
nodeSelector: {}
# Or simply omit optional values

Override Patterns

Hierarchy (lowest to highest priority)

  1. values.yaml in chart
  2. Parent chart's values.yaml (for subcharts)
  3. -f custom-values.yaml (left to right, last wins)
  4. --set key=value (highest priority)

Common Override Scenarios

# Production override file
helm install myapp . -f values-production.yaml

# Quick override with --set
helm install myapp . --set replicaCount=3 --set image.tag=v2.1.0

# Multiple value files (last wins)
helm install myapp . -f values-base.yaml -f values-production.yaml -f values-secrets.yaml

values-production.yaml Pattern

# Production overrides only — don't repeat defaults
replicaCount: 3

image:
  tag: "v2.1.0"
  pullPolicy: IfNotPresent

resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: "2"
    memory: 1Gi

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 20

ingress:
  enabled: true
  className: nginx
  hosts:
    - host: app.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: app-tls
      hosts:
        - app.example.com

Type Safety with values.schema.json

Basic Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["replicaCount", "image"],
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 1,
      "description": "Number of pod replicas"
    },
    "image": {
      "type": "object",
      "required": ["repository"],
      "properties": {
        "repository": {
          "type": "string",
          "minLength": 1,
          "description": "Container image repository"
        },
        "tag": {
          "type": "string",
          "description": "Image tag"
        },
        "pullPolicy": {
          "type": "string",
          "enum": ["Always", "IfNotPresent", "Never"],
          "description": "Image pull policy"
        }
      }
    },
    "service": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["ClusterIP", "NodePort", "LoadBalancer"],
          "description": "Kubernetes service type"
        },
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535,
          "description": "Service port number"
        }
      }
    }
  }
}

Why Use Schema

  • Fails fasthelm install rejects invalid values before rendering templates
  • Documents types — self-documenting valid options (enums, ranges)
  • IDE support — editors can autocomplete and validate values files
  • CI safety — catches typos in value overrides early

Testing Values

helm lint

# Basic lint
helm lint mychart/

# Lint with override values
helm lint mychart/ -f values-production.yaml

# Lint with --set
helm lint mychart/ --set replicaCount=0  # Should fail schema

helm template

# Render templates locally
helm template myrelease mychart/

# Render with overrides to verify
helm template myrelease mychart/ -f values-production.yaml

# Debug mode (shows computed values)
helm template myrelease mychart/ --debug

# Render specific template
helm template myrelease mychart/ -s templates/deployment.yaml

Checklist for New Values

Check Question
Documented? Does the key have an inline comment?
Default works? Can you helm install without overriding?
Type clear? Is it obvious if this is string, int, bool, list, map?
Overridable? Can it be set with --set? (avoid deeply nested)
No secrets? Are default values free of passwords/tokens?
camelCase? Does it follow Helm naming convention?
Flat enough? Is nesting 3 levels or less?