Skip to content
kix /docs
Install the CLI

How-to guide Author packages and clusters

Keep intentionally empty values

Make sure an empty map or list survives to the rendered manifest, and protect helpers that build one from optional arguments.

Some Kubernetes values are meaningful precisely because they are empty. emptyDir: {} selects a volume source. An empty label selector matches everything. subresources.status: {} enables a CRD subresource. Kix keeps these, but it also removes the empty leftovers that appear when every field inside a container is unset. This guide shows how to stay on the right side of that line.

A container you write empty renders exactly as written. No wrapper, no special syntax:

spec.template.spec.volumes = [
{
name = "tmp";
emptyDir = { };
}
];

This renders emptyDir: {} in the output. The same holds for lists: ingress = [ ]; renders ingress: []. Pasting a standard Kubernetes manifest into a package works without changes.

A container whose members are all null is treated as unset and omitted, along with any wrappers that end up empty because of it:

resources = {
limits = null;
requests = null;
};
# renders nothing: the container collapses and the field is absent

This is the shape merged, unset options produce, and removing it is what keeps rendered manifests free of resources: {} noise.

The trap sits between those two cases: a helper that builds a presence-meaningful container out of optional arguments. When every argument is null, the result is a container of nulls, and it collapses. The field disappears from the manifest without an error.

Wrap the result in kix.keep to pin it:

source.emptyDir = kix.keep {
medium = config.medium; # may be null
sizeLimit = config.sizeLimit; # may be null
};
# renders emptyDir: {} when both are null,
# emptyDir: { sizeLimit = "1Gi"; } when one is set

Filtering the nulls yourself works too, and is what kix.mount.emptyDir does internally:

source.emptyDir =
{ }
// lib.optionalAttrs (medium != null) { inherit medium; }
// lib.optionalAttrs (sizeLimit != null) { inherit sizeLimit; };

Prefer kix.keep when the container is assembled across several places or merged after construction: the protection travels with the value, so a later merge that introduces fresh nulls cannot collapse it.

Render the cluster and check the field survived:

# in the rendered YAML for the workload:
volumes:
- name: tmp
emptyDir: {}

The volumeHasSource scorecard rule backs this up for the volume case. It is part of the built-in rule set, which runs on every cluster by default, so a volume that would render without a source is reported with a message naming the volume and the fix.

The rule’s severity is error, but a cluster caps every finding at scorecard.maxSeverity, which defaults to warning. Set the cap to error when this finding should stop the build. See Fail the build on scorecard findings.