MutatingPolicy
The Kyverno MutatingPolicy
type extends the Kubernetes MutatingAdmissionPolicy
type for complex mutation operations and other features required for Policy-as-Code. A MutatingPolicy
is a superset of a MutatingAdmissionPolicy
and contains additional fields for Kyverno specific features.
Comparison with MutatingAdmissionPolicy
The table below compares major features across the Kubernetes MutatingAdmissionPolicy
and Kyverno MutatingPolicy
types.
Feature | MutatingAdmissionPolicy | MutatingPolicy |
---|---|---|
Enforcement | Admission | Admission, Background, Pipelines, … |
Payloads | Kubernetes | Kubernetes |
Distribution | Kubernetes | Helm, CLI, Web Service, API, SDK |
CEL Library | Basic | Extended |
Bindings | Manual | Automatic |
Auto-generation | - | Pod Controllers, MutatingAdmissionPolicy |
External Data | - | Kubernetes resources or API calls |
Caching | - | Global Context, image verification results |
Background scans | - | Periodic, On policy creation or change |
Exceptions | - | Fine-grained exceptions |
Reporting | - | Policy WG Reports API |
Testing | - | Kyverno CLI (unit), Chainsaw (e2e) |
Existing Resources | - | Background mutation support |
Additional Fields
The MutatingPolicy
extends the Kubernetes MutatingAdmissionPolicy with the following additional fields for Kyverno features. A complete reference is provided in the API specification.
evaluation
The spec.evaluation
field defines how the policy is applied and how the payload is processed. It can be used to enable, or disable, admission request processing and existing resource mutation for a policy.
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: sample
5spec:
6 evaluation:
7 admission:
8 enabled: true
9 mutateExisting:
10 enabled: false
11 ...
admission.enabled
: When set totrue
, the policy is applied during admission requests (CREATE, UPDATE operations). This is the primary use case for mutating resources as they are being created or updated.mutateExisting.enabled
: When set totrue
, the policy is applied to existing resources in the cluster through background processing. This allows you to mutate resources that already exist without requiring them to be recreated or updated.
Refer to the API Reference for details.
webhookConfiguration
The spec.webhookConfiguration
field defines properties used to manage the Kyverno admission controller webhook settings.
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-labels
5spec:
6 webhookConfiguration:
7 timeoutSeconds: 15
8 ...
In the policy above, webhookConfiguration.timeoutSeconds
is set to 15
, which defines how long the admission request waits for policy evaluation. The default is 10s
, and the allowed range is 1–30s
. After this timeout, the request may fail or ignore the result based on the failure policy. Kyverno reflects this setting in the generated MutatingWebhookConfiguration
.
Refer to the API Reference for details.
autogen
The spec.autogen
field defines policy auto-generation behaviors, to automatically generate policies for pod controllers and generate MutatingAdmissionPolicy
types for Kubernetes API server execution.
Here is an example of generating policies for deployments, jobs, cronjobs, and statefulsets and also generating a MutatingAdmissionPolicy
from the MutatingPolicy
declaration:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-default-labels
5spec:
6 autogen:
7 mutatingAdmissionPolicy:
8 enabled: true
9 podControllers:
10 controllers:
11 - deployments
12 - jobs
13 - cronjobs
14 - statefulsets
Generating a MutatingAdmissionPolicy
from a MutatingPolicy
provides the benefits of faster and more resilient execution during admission controls while leveraging all features of Kyverno.
Refer to the API Reference for details.
Kyverno CEL Libraries
Kyverno enhances Kubernetes’ CEL environment with libraries enabling complex mutation logic and advanced features. For comprehensive documentation of all available CEL libraries, see the CEL Libraries documentation.
Resource Library Examples
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-registry-labels
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE", "UPDATE"]
12 variables:
13 - name: cm
14 expression: >-
15 resource.Get("v1", "configmaps", "kube-system", "allowed-registry")
16 - name: allowedRegistry
17 expression: "variables.cm.data[?'registry'].orValue('')"
18 mutations:
19 - patchType: ApplyConfiguration
20 applyConfiguration:
21 expression: |
22 Object{
23 metadata: Object.metadata{
24 labels: {"registry": string(variables.allowedRegistry)}
25 }
26 }
HTTP Library Examples
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-external-team-labels
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE", "UPDATE"]
12 variables:
13 - name: externalData
14 expression: >-
15 http.Get("http://test-api-service.default.svc.cluster.local:80")
16 mutations:
17 - patchType: ApplyConfiguration
18 applyConfiguration:
19 expression: |
20 Object{
21 metadata: Object.metadata{
22 labels: {"external-team": string(variables.externalData.metadata.team)}
23 }
24 }
User Library Examples
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-service-account-labels
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE", "UPDATE"]
12 variables:
13 - name: sa
14 expression: parseServiceAccount(request.userInfo.username)
15 mutations:
16 - patchType: ApplyConfiguration
17 applyConfiguration:
18 expression: |
19 Object{
20 metadata: Object.metadata{
21 labels: {
22 "service-account": string(variables.sa.Name),
23 "namespace": string(variables.sa.Namespace)
24 }
25 }
26 }
Image Library Examples
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-image-labels
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE", "UPDATE"]
12 variables:
13 - name: images
14 expression: >-
15 object.spec.containers.map(e, image(e.image))
16 + object.spec.?initContainers.orValue([]).map(e, image(e.image))
17 + object.spec.?ephemeralContainers.orValue([]).map(e, image(e.image))
18 - name: firstImage
19 expression: "variables.images[0]"
20 mutations:
21 - patchType: ApplyConfiguration
22 applyConfiguration:
23 expression: |
24 Object{
25 metadata: Object.metadata{
26 labels: {
27 "image-registry": string(variables.firstImage.registry()),
28 "image-repository": string(variables.firstImage.repository())
29 }
30 }
31 }
GlobalContext Library Examples
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-global-context-labels
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE", "UPDATE"]
12 variables:
13 - name: globalData
14 expression: >-
15 globalContext.Get("team-cluster-values", "")
16 mutations:
17 - patchType: ApplyConfiguration
18 applyConfiguration:
19 expression: |
20 Object{
21 metadata: Object.metadata{
22 labels: {
23 "team": string(variables.globalData.team),
24 "environment": string(variables.globalData.environment)
25 }
26 }
27 }
Patches with ApplyConfiguration
ApplyConfiguration patches use a merge-style approach, written as CEL expressions that return an Object. This method is more intuitive and less error-prone than JSONPatch for most mutations.
Basic ApplyConfiguration
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-service-account
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE"]
12 mutations:
13 - patchType: ApplyConfiguration
14 applyConfiguration:
15 expression: >
16 Object{
17 metadata: Object.metadata{
18 labels: Object.metadata.labels{
19 foo: "bar"
20 }
21 }
22 }
Conditional ApplyConfiguration
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: conditional-labels
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE", "UPDATE"]
12 mutations:
13 - patchType: ApplyConfiguration
14 applyConfiguration:
15 expression: |
16 has(object.metadata.labels) && has(object.metadata.labels.environment) ?
17 Object{
18 metadata: Object.metadata{
19 labels: {"managed": "true"}
20 }
21 } :
22 Object{
23 metadata: Object.metadata{
24 labels: {"environment": "dev", "managed": "true"}
25 }
26 }
RFC 6902 JSON Patches
JSONPatch provides fine-grained control over mutations using RFC 6902 JSON Patch operations. This method is useful for complex mutations that require precise control over the patch operations.
Basic JSONPatch
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: add-labels-jsonpatch
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE"]
12 mutations:
13 - patchType: JSONPatch
14 jsonPatch:
15 expression: |
16 has(object.metadata.labels) ?
17 [
18 JSONPatch{
19 op: "add",
20 path: "/metadata/labels/managed",
21 value: "true"
22 }
23 ] :
24 [
25 JSONPatch{
26 op: "add",
27 path: "/metadata/labels",
28 value: {"managed": "true"}
29 }
30 ]
Multiple JSONPatch Operations
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: complex-jsonpatch
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE", "UPDATE"]
12 mutations:
13 - patchType: JSONPatch
14 jsonPatch:
15 expression: |
16 [
17 JSONPatch{
18 op: "add",
19 path: "/metadata/labels/environment",
20 value: "production"
21 },
22 JSONPatch{
23 op: "add",
24 path: "/metadata/annotations/kyverno.io~1managed",
25 value: "true"
26 },
27 JSONPatch{
28 op: "replace",
29 path: "/spec/securityContext/runAsNonRoot",
30 value: true
31 }
32 ]
Conditional JSONPatch with Escape
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: escape-jsonpatch
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE"]
12 mutations:
13 - patchType: JSONPatch
14 jsonPatch:
15 expression: |
16 [
17 JSONPatch{
18 op: "add",
19 path: "/metadata/labels/" + jsonpatch.escapeKey("app.kubernetes.io/name"),
20 value: "my-app"
21 }
22 ]
Looping with CEL Functions
Kyverno supports looping through elements within resources using CEL functions like map()
, allowing you to apply mutations to multiple elements within a resource.
Iterating Through Containers
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: foreach
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [ "" ]
9 apiVersions: [ "v1" ]
10 operations: [ "CREATE" ]
11 resources: [ "pods" ]
12 mutations:
13 - patchType: ApplyConfiguration
14 applyConfiguration:
15 expression: >
16 Object{
17 spec: Object.spec{
18 containers: object.spec.containers.map(container, Object.spec.containers{
19 name: container.name,
20 securityContext: Object.spec.containers.securityContext{
21 allowPrivilegeEscalation: false
22 }
23 })
24 }
25 }
This example uses CEL’s map()
function to iterate through all containers in a pod and add a securityContext
with allowPrivilegeEscalation: false
to each container.
Conditional Iteration with Filter
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: foreach-conditional
5spec:
6matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE", "UPDATE"]
12 mutations:
13 - patchType: JSONPatch
14 jsonPatch:
15 expression: |
16 object.spec.containers.map(c,
17 c.image.startsWith("nginx:") ?
18 JSONPatch{
19 op: "add",
20 path: "/spec/containers/" + string(object.spec.containers.indexOf(c)) + "/env",
21 value: [{"name": "NGINX_ENV", "value": "production"}]
22 } : null
23 ).filter(p, p != null)
This example uses CEL’s map()
and filter()
functions to conditionally add environment variables only to containers that use nginx images.
Mutating Existing Resources
MutatingPolicy supports background processing to mutate existing resources in the cluster, not just during admission. This is enabled by setting spec.evaluation.mutateExisting.enabled: true
.
Important Considerations
Asynchronous Processing: Mutation for existing resources is an asynchronous process. There will be a variable amount of delay between when the trigger is observed and when the existing resource is mutated.
Custom Permissions: Custom permissions are almost always required. Because these mutations occur on existing resources and not during an AdmissionReview, Kyverno may need additional permissions which it does not have by default. See the section on customizing permissions for how to grant additional permissions to the Kyverno background controller’s ServiceAccount. Kyverno will perform these permissions checks when a mutate existing policy is installed.
Same Trigger and Target
When the trigger and target are the same resource type, the policy will mutate ALL existing resources of that type when a trigger occurs:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: same-trigger-target
5spec:
6 evaluation:
7 mutateExisting:
8 enabled: true
9 matchConstraints:
10 resourceRules:
11 - apiGroups: [ "" ]
12 apiVersions: [ "v1" ]
13 operations: [ "CREATE", "UPDATE"]
14 resources: [ "namespaces" ]
15 mutations:
16 - patchType: ApplyConfiguration
17 applyConfiguration:
18 expression: >
19 Object{
20 metadata: Object.metadata{
21 labels: Object.metadata.labels{
22 foo: "bar"
23 }
24 }
25 }
In this example, when any namespace is created or updated, Kyverno will:
- Mutate the namespace that triggered the policy
- Fetch and mutate ALL existing namespaces that match the criteria
This means if you have 10 existing namespaces and create a new one, all 11 namespaces will be mutated to add the foo: "bar"
label.
Different Trigger and Target
You can specify different trigger and target resources using targetMatchConstraints
. When a trigger occurs, Kyverno will mutate ALL existing resources that match the target criteria:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: different-trigger-target
5spec:
6 evaluation:
7 mutateExisting:
8 enabled: true
9 matchConstraints:
10 resourceRules:
11 - apiGroups: [ "" ]
12 apiVersions: [ "v1" ]
13 operations: [ "CREATE", "UPDATE"]
14 resources: [ "namespaces" ]
15 resourceNames: ["test-namespace"]
16 targetMatchConstraints:
17 namespaceSelector:
18 matchLabels:
19 test: "enabled"
20 resourceRules:
21 - apiGroups: [ "" ]
22 apiVersions: [ "v1" ]
23 operations: [ "CREATE", "UPDATE"]
24 resources: [ "configmaps" ]
25 mutations:
26 - patchType: ApplyConfiguration
27 applyConfiguration:
28 expression: >
29 Object{
30 metadata: Object.metadata{
31 labels: Object.metadata.labels{
32 foo: "bar"
33 }
34 }
35 }
In this example:
- Trigger: When the namespace
test-namespace
is created or updated - Target: ALL existing ConfigMaps in namespaces with the label
test: enabled
will be mutated to add thefoo: "bar"
label
This means if you have 5 ConfigMaps in matching namespaces and update the trigger namespace, all 5 ConfigMaps will be mutated.
Mutate Ordering
The order of mutations is important when multiple mutations are applied. Kyverno processes mutations in the order they appear in the policy. However, the order is not guaranteed across multiple MutatingPolicies - if multiple policies apply to the same resource, their execution order is not deterministic.
Sequential Mutations
When multiple mutations are defined within the same policy, they are processed in order. This allows you to create mutations that depend on the results of previous mutations:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: simple-database-policy
5spec:
6 matchConstraints:
7 resourceRules:
8 - apiGroups: [""]
9 apiVersions: ["v1"]
10 resources: ["pods"]
11 operations: ["CREATE"]
12 mutations:
13 # First mutation
14 - patchType: ApplyConfiguration
15 applyConfiguration:
16 expression: |
17 object.spec.containers.exists(c, c.image.contains("cassandra") || c.image.contains("mongo")) ?
18 Object{
19 metadata: Object.metadata{
20 labels: Object.metadata.labels{
21 type: "database"
22 }
23 }
24 } : Object{}
25
26 # Second mutation
27 - patchType: ApplyConfiguration
28 applyConfiguration:
29 expression: |
30 object.metadata.labels.type == "database" ?
31 Object{
32 metadata: Object.metadata{
33 labels: Object.metadata.labels{
34 backup: "yes"
35 }
36 }
37 } : Object{}
In this example:
- First mutation: Checks if any container uses cassandra or mongo images and adds
type: database
label - Second mutation: Checks if the
type: database
label exists, then addsbackup: yes
The mutations must be ordered from top to bottom in the order of their dependencies within the same policy to ensure consistent results.
Reinvocation Policy
The reinvocationPolicy
controls whether mutations are reapplied when earlier mutations modify the object:
1apiVersion: policies.kyverno.io/v1alpha1
2kind: MutatingPolicy
3metadata:
4 name: reinvocation-example
5spec:
6 reinvocationPolicy: IfNeeded
7 matchConstraints:
8 resourceRules:
9 - apiGroups: [""]
10 apiVersions: ["v1"]
11 resources: ["pods"]
12 operations: ["CREATE"]
13mutations:
14 - patchType: ApplyConfiguration
15 applyConfiguration:
16 expression: |
17 Object{
18 metadata: Object.metadata{
19 labels: {"stage": "initial"}
20 }
21 }
Reinvocation Policy Options:
Never
: Apply mutation once per binding (default)IfNeeded
: Re-apply if a previous mutation modifies the object
The IfNeeded
policy is useful when mutations depend on one another, as it ensures that subsequent mutations can react to changes made by earlier mutations.
Mutation with Generation
GitOps Considerations
When using MutatingPolicy in GitOps workflows, the same considerations apply as with ClusterPolicy mutate rules. MutatingPolicy can interoperate with GitOps solutions, but there are important considerations to prevent “fighting” between the mutating admission controller and GitOps controllers.
For comprehensive guidance on GitOps integration, including specific configurations for Flux and ArgoCD, see the GitOps Considerations section in the ClusterPolicy mutate documentation.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.