← Back to Blog
BEST PRACTICES

Helm Value Naming Conventions: A Data-Driven Approach

January 24, 202612 min read

Should it be imagePullPolicy or image.pullPolicy? After analyzing 1,589 Helm charts, the data shows clear patterns—and they might surprise you.

The Great Debate

When building a Helm chart, you face hundreds of naming decisions. Flatten everything at the top level? Use deep nesting? camelCase or snake_case? The Helm documentation is surprisingly quiet on these questions.

So we turned to the data: what do the most popular charts actually do?

The Data Set

We analyzed values.yaml files from the top charts on Artifact Hub, covering applications from NGINX to PostgreSQL to custom business applications. The results reveal strong, consistent patterns.

Finding #1: Nested Wins Over Flat

The overwhelming pattern is nested configuration, not flat key names:

# ✅ Standard Pattern (78.5% of charts)
image:
  repository: nginx
  tag: 1.25.3
  pullPolicy: IfNotPresent

# ❌ Flat Pattern (rare)
imageRepository: nginx
imageTag: 1.25.3
imagePullPolicy: IfNotPresent

Why nesting wins:

  • Groups related values together visually
  • Reduces top-level clutter
  • Makes it trivial to override entire sections (--set image.tag=2.0.0)
  • Mirrors Kubernetes resource structure

Key takeaway: Use nested objects for logical groupings. Only flatten when you have a single, standalone value.

Finding #2: camelCase is Standard (with exceptions)

Within nested objects, camelCase dominates:

# ✅ Standard (95%+ of charts)
service:
  type: ClusterIP
  port: 80

serviceAccount:
  create: true
  name: ""

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 10

# ❌ Rare alternatives
service:
  type: ClusterIP
  port: 80

service_account:
  create: true
  name: ""

auto_scaling:
  enabled: false
  min_replicas: 1
  max_replicas: 10

Exception: Top-level keys often use simple nouns without case transformation:image, service, ingress, resources.

Finding #3: Follow Kubernetes Resource Names

When your value corresponds to a Kubernetes resource field, use the exact same name:

# In Kubernetes Pod spec:
spec:
  containers:
  - name: app
    resources:
      limits:
        cpu: 100m
        memory: 128Mi
      requests:
        cpu: 100m
        memory: 128Mi

# In values.yaml - EXACT MATCH:
resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

This applies to dozens of common fields: nodeSelector, tolerations,affinity, podAnnotations, securityContext.

Why it matters: Users familiar with Kubernetes know exactly what these values do. Documentation is minimal because the Kubernetes docs already exist.

Finding #4: Boolean Flags End in "enabled"

When you need a toggle, use .enabled:

# ✅ Standard Pattern
ingress:
  enabled: false
  hosts: [...]

autoscaling:
  enabled: false
  minReplicas: 1

persistence:
  enabled: false
  size: 8Gi

serviceAccount:
  create: true  # "create" is also common for resource creation

# ❌ Avoid
ingress:
  enable: false       # Not "enable"
  active: false       # Not "active"

autoscaling:
  on: false           # Not "on"
  disabled: true      # Double negative is confusing

Pattern: enabled for features, create for resources.

Finding #5: Use Descriptive Top-Level Keys

Top-level organization in the most popular charts:

# Common top-level structure (in order of frequency):
image:           # Container image configuration
replicaCount:    # Number of pods
service:         # Service configuration
ingress:         # Ingress configuration
resources:       # CPU/memory limits and requests
autoscaling:     # HPA configuration
nodeSelector:    # Node selection
tolerations:     # Pod tolerations
affinity:        # Pod affinity/anti-affinity
serviceAccount:  # ServiceAccount configuration
podAnnotations:  # Annotations to add to pods
podSecurityContext:  # Security context for pods
securityContext:     # Security context for containers
persistence:     # PersistentVolumeClaim configuration
env:             # Environment variables
extraEnv:        # Additional environment variables
configMap:       # ConfigMap configuration
secret:          # Secret configuration

Notice the pattern: each top-level key is either a Kubernetes resource type (service,ingress) or a clear noun describing what it configures (image, persistence).

Finding #6: "nameOverride" vs "fullnameOverride"

These two values appear in 50%+ of charts and have specific, different purposes:

nameOverride: ""      # Overrides the chart name portion
fullnameOverride: ""  # Overrides the entire generated name

# How they work with release "myapp" and chart "nginx":
# Default name: myapp-nginx

# With nameOverride: "web"
# Result: myapp-web

# With fullnameOverride: "production-web"
# Result: production-web

Best practice: Always include both. Users expect them.

Finding #7: Arrays Use Plural Names

When a value is an array, use plural:

# ✅ Standard
ingress:
  hosts:    # Plural - it's an array
    - host: example.com
      paths: [/]

tolerations: []  # Plural
affinity: {}     # Singular - it's an object

extraVolumes: []       # Plural
extraVolumeMounts: []  # Plural

# ❌ Avoid
ingress:
  host:     # Singular but expects array - confusing
    - host: example.com

Finding #8: Prefix Custom/Advanced Options

When you need to expose advanced configuration that goes beyond the standard values, use a prefix to signal it's non-standard:

# Standard values:
env:
  - name: DATABASE_URL
    value: postgres://...

# Advanced/custom values:
extraEnv:
  - name: CUSTOM_FEATURE
    value: enabled

extraVolumes: []
extraVolumeMounts: []
extraArgs: []
extraInitContainers: []

# Alternative prefix: "additional"
additionalLabels: {}
additionalAnnotations: {}

Why this works: Users know that extra* values are for edge cases. The standard values handle 90% of use cases, extra values handle the remaining 10%.

Finding #9: Avoid Deep Nesting (3 levels max)

Most values are 2 levels deep. Some go to 3. Almost none go deeper:

# ✅ Good - 2 levels
image.repository
service.type
resources.limits.cpu  # 3 levels is fine for Kubernetes mirrors

# ❌ Too deep - hard to read and override
deployment.spec.template.metadata.labels.app
# Better: expose as top-level
podLabels:
  app: myapp

Finding #10: Document Non-Obvious Defaults

When a value has a non-obvious default or behavior, add a comment:

# ✅ Good
serviceAccount:
  create: true
  # If not set, a name is generated using the fullname template
  name: ""

image:
  repository: nginx
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion
  tag: ""

# ❌ Less helpful
serviceAccount:
  create: true
  name: ""

image:
  repository: nginx
  pullPolicy: IfNotPresent
  tag: ""

Real-World Example: Comparing Two Approaches

Here's a real comparison from popular charts—same functionality, different naming:

# Chart A (follows conventions)
image:
  repository: myapp
  tag: 1.0.0
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  hosts:
    - host: chart-example.local
      paths: [/]

resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

# Chart B (non-standard)
containerImage: myapp:1.0.0
imagePullPolicy: IfNotPresent

serviceType: ClusterIP
servicePort: 80

enableIngress: false
ingressHost: chart-example.local
ingressPath: /

cpuLimit: 100m
memoryLimit: 128Mi
cpuRequest: 100m
memoryRequest: 128Mi

Chart A is easier to understand, easier to override, and matches user expectations. Chart B requires reading documentation for every value.

The Checklist: Follow These Rules

When naming values in your Helm chart:

  1. ✅ Use nested objects for logical groupings (image.repository, not imageRepository)
  2. ✅ Use camelCase within objects (pullPolicy, serviceAccount)
  3. ✅ Match Kubernetes resource field names exactly when applicable
  4. ✅ Use .enabled for feature toggles, .create for resource creation
  5. ✅ Use plural names for arrays (hosts, tolerations)
  6. ✅ Keep nesting to 2-3 levels maximum
  7. ✅ Use extra* prefix for advanced/custom options
  8. ✅ Include nameOverride and fullnameOverride
  9. ✅ Add comments for non-obvious defaults
  10. ✅ Use descriptive top-level keys that match Kubernetes resources

Testing Your Naming

Before publishing your chart, ask:

  • Can a Kubernetes user guess what this value does without reading docs?
  • Does this match the equivalent field in the Kubernetes resource spec?
  • Would --set overrides feel natural? (--set image.tag=2.0.0)
  • Are related values grouped together?

If the answer is yes to all four, you're following conventions.

When to Break the Rules

Sometimes you should deviate from conventions:

  • Your application has domain-specific terminology that users expect
  • You're maintaining backward compatibility with an existing chart
  • A flat structure genuinely makes more sense (rare, but possible)

Just be intentional about it, and document why you made that choice.

Explore More

Want to see how specific values are named in real charts? Use our search tool to explore 847 values from 1,589 charts, complete with usage percentages and examples.