Conventions
Merge order
Labels and annotations merge in three layers: global, then per-resource, then chart-generated. Each layer merges per key on top of the previous one, so a per-resource key overrides the same global key while the rest of the global map survives; it never replaces the whole map.
annotations: # layer 1: every resource
meta.example.com/owner: platform@example.com
meta.example.com/tier: standard
services:
app:
annotations: # layer 2: overrides tier, keeps owner
meta.example.com/tier: premiumThe chart-generated layer always wins: identity labels (app.kubernetes.io/*, helm.sh/chart) and lifecycle annotations (checksum/*, helm.sh/hook*, helm.sh/resource-policy) ignore user values, so no values file can break selectors or Helm's lifecycle handling. The exceptions and per-surface details live on the Labels and Annotations pages.
Container configuration resolves along its own cascade, container < pod < chart; clearing an inherited value is covered in its own section below.
Bare resource keys
deployments, cronJobs, and jobs all support bare keys (null value) and produce a working resource out of the box:
deployments:
myapp: # one container named "myapp", global image settings applied
cronJobs:
backup: # schedule: "@daily", restartPolicy: Never, one container named "backup"
jobs:
migrate: # restartPolicy: Never, one container named "migrate"The container is named after the key and inherits all global defaults (image.*, replicaCount, imagePullPolicy, configure.*, etc.). This is the minimal way to define a resource and is equivalent to explicitly naming the container:
deployments:
myapp:
containers:
myapp:Note: services and ingresses do not follow this convention: a bare service has no ports, and a bare ingress renders nothing (hosts are required).
Disabling resources with enabled: false
Every named resource (Deployments, Services, Ingresses, CronJobs, Jobs, and HPAs) supports an enabled flag. Setting it to false skips rendering that resource entirely without removing it from values:
deployments:
api:
containers:
api:
image: myorg/api:latest
worker:
enabled: false # exists in values, renders nothing; useful for feature flags or environment overrides
containers:
worker:
image: myorg/worker:latest
services:
api:
debug:
enabled: false # disable a service without deleting its configuration
ingresses:
public:
hosts: [myapp.example.com]
internal:
enabled: falseThis is particularly useful in multi-environment setups where a resource is needed in production but not in preview deployments:
# values.production.yaml
deployments:
worker:
enabled: true
# values.preview.yaml
deployments:
worker:
enabled: falseClearing inherited values with [] and {}
At the pod level, volumeMounts and other list/dict fields cascade down to containers. A container can opt out of inherited values entirely by setting the field to an empty list or dict:
deployments:
app:
volumeMounts:
- name: data
mountPath: /data
containers:
main:
# inherits volumeMounts from pod level → gets /data mount
sidecar:
volumeMounts: [] # explicitly clears pod-level volumeMounts: sidecar gets nothingThe same pattern applies to configure flags: set configure.persistence: false on a container to disable auto-mounting even when persistence is enabled at the pod level:
deployments:
app:
configure:
persistence: true
containers:
main:
# inherits configure.persistence: true → gets PVC mount
cache:
configure:
persistence: false # opt out: cache container gets no PVC mount{} and [] are standard YAML for an empty map and empty list. Use [] to clear any inherited list, {} to clear any inherited map.