Missing default value in nested field of kubernetes custom resource
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Kubernetes CRDs support schema-based default values, but nested fields often surprise people. A default on a child field is only applied when the parent object exists in the submitted resource, so a missing parent usually means the nested default never runs.
Why nested defaults appear to be missing
CRD defaulting is driven by the OpenAPI schema on the API server. When a request creates or updates a custom resource, Kubernetes walks the object that was actually sent and applies defaults where the schema says a value is missing.
The important detail is that defaulting does not invent arbitrary parent objects just because a grandchild has a default. If spec.settings is absent, Kubernetes cannot descend into that branch and apply a default to spec.settings.mode.
Consider this CRD fragment:
And this custom resource:
The resulting object still has no spec.settings.mode, because settings itself was never present.
How to make nested defaults work
There are three common fixes, and the right one depends on how much control you want in the schema.
1. Default the parent object too
If the parent can safely exist as an empty object, give it a default of object.
Now a submitted resource with an empty spec allows Kubernetes to materialize settings, and then the nested mode default can apply.
2. Default the whole nested structure explicitly
If you already know the desired shape, default the entire branch in one place.
This is often easier to reason about because you are defining the final structure directly instead of depending on multiple defaulting steps.
3. Use an admission webhook for complex logic
Schema defaults are static. If the default depends on another field, cluster state, or versioned business logic, use a mutating admission webhook instead.
A webhook adds operational overhead, but it gives full control and keeps defaults consistent even when the rules become more involved.
How to verify the behavior
After applying the CRD, create a minimal custom resource and inspect the stored object:
If the default was applied, you should see the populated field in the server response. If not, inspect the CRD schema itself:
This helps catch indentation mistakes, wrong property paths, or defaults placed under the wrong version block.
Common Pitfalls
- Expecting a nested child default to apply when the parent object is completely missing.
- Adding defaults to CRD schemas on an older cluster version that does not support the behavior you expect.
- Forgetting that defaults are server-side. Client-side YAML validation will not show the final stored object.
- Using schema defaults for logic that depends on external state. That is a webhook problem, not a schema problem.
- Defining incompatible defaults that violate the schema, such as a string default for a field declared as an integer.
Summary
- CRD defaults are applied by the API server from the OpenAPI schema.
- A nested default only works when Kubernetes can reach that branch in the submitted object.
- If the parent object is missing, child defaults are usually skipped.
- Defaulting the parent object or the whole nested structure is the simplest fix.
- Use a mutating webhook when defaults depend on custom logic rather than static schema values.

