foreach capability lets you iterate list elements and apply the same mutation to each item.
A common real-world requirement: the security team mandates that every container (including init containers) must have a securityContext that prevents running as root. A naive patchStrategicMerge against the containers block would replace the entire list and break multi-container Pods. A JSON Patch is also problematic because it normally requires knowing every container index up front.

/spec/containers/0 and /spec/containers/1, but you can’t predict how many containers a Pod will define. The solution is Kyverno’s foreach declaration.
What forEach provides
- Define the list to loop over using a JMESPath expression, for example:
request.object.spec.containers. - Kyverno exposes the current item as
elementwithin the loop. You can referenceelement.name,element.image, and other fields. - You may apply either a
patchStrategicMergeorpatchesJson6902; Kyverno executes the specified patch once for every item in the list.
Minimal
patchStrategicMerge example (iterates containers and references the current element):
patchesJson6902) inside a foreach, Kyverno also exposes elementIndex—the zero-based index of the current list item. This is useful because JSON Patch requires exact numeric indices in paths.
Example: add a securityContext to every container using patchesJson6902:
The
elementIndex variable lets you construct precise JSON Patch paths such as /spec/containers/0/securityContext, /spec/containers/1/securityContext, and so on.patchesJson6902 is precise, patchStrategicMerge is often more readable and aligns with Kubernetes patch semantics. To update the correct list element with a strategic merge patch, Kyverno needs a way to match the list item—typically by using the container name as a conditional anchor.
Pattern summary:
- Use
(name): "{{ element.name }}"to match the specific container in the existing list. - Use the
+()add-if-not-present anchor to add fields only when they don’t already exist (so you don’t overwrite user-set values).
allowPrivilegeEscalation is set to false only when the field is absent:
- The
foreachloop iterates each container in the incoming Pod. - For each
element, Kyverno finds the container with the matchingname. - The
+(allowPrivilegeEscalation): falseanchor adds the field only if it’s missing; existing values are preserved.
Combining a conditional anchor to identify the list element by
name with the add-if-not-present anchor (+()) is a safe, common pattern when using patchStrategicMerge inside foreach.- The conditional anchor
(name): "{{ element.name }}"selects the correct container in the Pod. +(memory)and+(cpu)add default values only when those request fields are not already present.
- Use
foreachto safely mutate each item in a list (e.g., containers) without replacing the entire list. - Choose
patchStrategicMergefor readability and declarative merges; use conditional anchors and+()to avoid overwriting. - Use
patchesJson6902when you need index-based JSON paths;elementIndexis provided to construct those paths. elementandelementIndexare the two variables Kyverno exposes inside aforeachloop—use them to reference the current item and its position.
- Kyverno documentation: mutate policies
- JMESPath: query language for JSON
- JSON Patch (RFC 6902)
- Kubernetes strategic merge patch