In a Kyverno pattern over spec.containers, how many containers must match the sub-pattern?
answer
- a list pattern is not a filter
- the default quantifier is every element
- the caret anchor means at least one
- init containers are a separate array
- an optional list needs the equality anchor
basics
~20 sAll of them. A Kyverno list pattern is applied to every element of the resource array, so one container without a memory limit fails the rule. The existence anchor on the array key relaxes it to at least one element.
solid answer
~40 sBy default a pattern element written under a list applies to **every** element of that list in the resource, so a single container missing `resources.limits.memory` fails the rule and the pod is rejected. That universal behaviour is what a limits rule needs. The existence anchor, written on the array key as `^(containers)`, flips it to "at least one element must match" — right for "the pod must have at least one container doing X", wrong for a limits rule, where it would let a compliant sidecar cover for a non-compliant app container. Two practical corollaries: `initContainers` is a separate array and needs its own block, and that block must carry an equality anchor, `=(initContainers)`, or pods that legitimately have no init containers are rejected for the absence of the key itself.
code
yaml · 12 linespattern:
spec:
containers: # applied to EVERY element of the array
- resources:
limits:
memory: "?*"
ephemeral-storage: "?*"
=(initContainers): # optional list: checked only when present
- resources:
limits:
memory: "?*"
ephemeral-storage: "?*"go deeper
Remember that a sub-pattern written under a list applies to every element of that list, so one non-compliant container is enough to fail the whole pod.
Explain the three quantifiers you can express: universal by default, existential with the caret anchor on the array key, and optional-but-validated with the equality anchor on the list itself.
Show that you check the containers, initContainers and volumes shapes separately, and that you can say why the existence anchor would silently gut a limits rule while looking correct in review.
Own the review standard for list-shaped rules across the library, since existence and equality anchors are where a rule quietly stops covering part of the estate without anything turning red.
## Universal by default When a Kyverno pattern contains an array, the element you write is a template applied to every element of the corresponding array on the resource. Write one container sub-pattern and it must hold for container zero, container one and container seventeen. This is the right default for a guardrail: a rule that requires a memory limit means *every* container, or it means nothing, because the one container you did not cover is the one that will eat the node. Many authors arrive expecting the opposite — that a list pattern is a filter, matching if some element matches. That intuition comes from query languages, and it is exactly backwards here. ## The existence anchor flips the quantifier `^()` is the anchor that changes the quantifier from *all* to *at least one*, and it is written on the key whose value is the array: ```yaml pattern: spec: ^(containers): - name: "audit-sidecar" ``` That says the pod must have at least one container named `audit-sidecar`; the others are unconstrained. It only makes sense for existence claims. Applied to a limits rule it becomes actively dangerous, because one well-behaved container satisfies the rule on behalf of every badly-behaved one in the same pod — a policy that reports green while the thing it was written to prevent ships every day. The question to ask before reaching for `^()` is which sentence you mean: "every container must…" or "the pod must contain a container that…". Only the second is an existence claim. ## Optional lists and the absence trap A pod may have no `initContainers` key at all. If your pattern names `initContainers` unanchored, then by the ordinary rule that unanchored keys are required, every pod without init containers is rejected — for the absence of the key, not for anything about limits. Your rule now blocks a large fraction of legitimate workloads and the message will point at a path developers cannot make sense of. The fix is the equality anchor: ```yaml pattern: spec: containers: - resources: limits: memory: "?*" =(initContainers): - resources: limits: memory: "?*" ``` `=(initContainers)` means: if the key is absent, check nothing; if it is present, the element sub-pattern must hold for every init container. Presence stays optional; correctness does not. The same shape recurs one level deeper for volumes. A rule that every `emptyDir` volume declares a `sizeLimit` is written as an optional `volumes` list whose elements carry an optional `emptyDir` object — `=(volumes)` with `=(emptyDir)` inside it — because most volumes are not `emptyDir` and a pod may have no volumes at all. Get the anchoring wrong there and you either reject every pod with a ConfigMap volume, or you assert nothing. ## What the developer is told Because the check is per element, the rejection message names the index: `failed at path /spec/containers/2/resources/limits/memory/`. That index is genuinely useful — it distinguishes "you forgot limits on the app container" from "your injected sidecar has none", which are different conversations with different owners. If you use a conditional anchor to skip an element, the indices in the message still refer to positions in the resource, so they remain a reliable pointer. ## Summary Default is universal, `^()` makes it existential, `=()` makes the list itself optional, and each of the three says something different about the policy you are actually writing. Choosing among them is the whole content of a list-shaped pattern.
- What would ^(containers) do to a memory-limit rule?It would break it. The existence anchor requires only one element of the array to match, so a single container with limits would satisfy the rule for the whole pod and every other container could ship with none. `^()` belongs to existence claims — the pod must contain a container that does X — never to a rule that must hold for all of them.
- Why does an unanchored initContainers key reject pods that have none?Because unanchored keys in a pattern are mandatory, and that applies to the array key itself, not only to fields inside the elements. A pod with no `initContainers` fails on the missing key. Writing `=(initContainers)` makes the list optional while still requiring every element that does exist to satisfy the sub-pattern.
- How would you require a sizeLimit on every emptyDir volume?Nest two equality anchors: `=(volumes)` because a pod may have none, and inside each element `=(emptyDir)` because most volumes are some other type, with `sizeLimit: "?*"` unanchored underneath as the actual assertion. Volumes that are not `emptyDir` are skipped by the inner anchor; every `emptyDir` that exists must declare a size.
saying these in an interview costs you the question
- Assumes one matching element satisfies a list pattern
- Uses the existence anchor where every element must comply
- Forgets initContainers is a separate array
- Leaves an optional list unanchored and blocks normal pods
- Treats a list pattern as a selector or filter