Helm Value Naming Conventions: A Data-Driven Approach
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: IfNotPresentWhy 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: 10Exception: 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: 128MiThis 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 confusingPattern: 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 configurationNotice 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-webBest 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.comFinding #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: myappFinding #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: 128MiChart 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:
- ✅ Use nested objects for logical groupings (
image.repository, notimageRepository) - ✅ Use camelCase within objects (
pullPolicy,serviceAccount) - ✅ Match Kubernetes resource field names exactly when applicable
- ✅ Use
.enabledfor feature toggles,.createfor resource creation - ✅ Use plural names for arrays (
hosts,tolerations) - ✅ Keep nesting to 2-3 levels maximum
- ✅ Use
extra*prefix for advanced/custom options - ✅ Include
nameOverrideandfullnameOverride - ✅ Add comments for non-obvious defaults
- ✅ 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
--setoverrides 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.