Skip to content

MutatingPolicy

FEATURE STATE:Stable•Kyverno v1.20.0

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.

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-label
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE']
resources: ['pods']
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
foo: "bar"
}
}
}

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

The MutatingPolicy extends the Kubernetes MutatingAdmissionPolicy with the following additional fields for Kyverno features. A complete reference is provided in the API specification.

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.

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: sample
spec:
evaluation:
admission:
enabled: true
mutateExisting:
enabled: false
# ...
  • admission.enabled: When set to true, 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 to true, 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.

  • spec.evaluation.mode: Either Kubernetes (default) or JSON. In JSON mode the policy uses JSONPatch expressions against object-root JSON payloads, which do not require Kubernetes metadata. See JSON Payloads.

Refer to the API Reference for details.

The spec.webhookConfiguration field defines properties used to manage the Kyverno admission controller webhook settings.

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-labels
spec:
webhookConfiguration:
timeoutSeconds: 15
# ...

In the policy above, webhookConfiguration.timeoutSeconds is set to 15, which defines how long the admission request waits for policy evaluation. The default is 10 seconds, and the allowed range is 1 to 30 seconds. After this timeout, the request will 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.

The spec.autogen field defines policy auto-generation behaviors, to automatically generate policies for pod controllers or generate MutatingAdmissionPolicy types for Kubernetes API server execution.

Here is an example of generating policies for deployments, jobs, cronjobs, and statefulsets from the MutatingPolicy declaration:

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-default-labels
spec:
autogen:
podControllers:
controllers:
- deployments
- jobs
- cronjobs
- statefulsets

Here is an example of generating a MutatingAdmissionPolicy from the MutatingPolicy declaration:

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-default-labels
spec:
autogen:
mutatingAdmissionPolicy:
enabled: true

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.

MutatingPolicy comes in both cluster-scoped and namespaced versions:

  • MutatingPolicy: Cluster-scoped, applies to resources across all namespaces
  • NamespacedMutatingPolicy: Namespace-scoped, applies only to resources within the same namespace

Both policy types have identical functionality and field structure. The only difference is the scope of resource selection.

apiVersion: policies.kyverno.io/v1
kind: NamespacedMutatingPolicy
metadata:
name: add-labels
namespace: production
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['pods']
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
environment: "production",
managed: "true"
}
}
}

As the name suggests, the NamespacedMutatingPolicy allows namespace owners to manage mutation policies without requiring cluster-admin permissions, improving multi-tenancy and security isolation.

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.

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.

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-service-account
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE']
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
foo: "bar"
}
}
}
apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: conditional-labels
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE', 'UPDATE']
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: |
has(object.metadata.labels) && has(object.metadata.labels.environment) ?
Object{
metadata: Object.metadata{
labels: {"managed": "true"}
}
} :
Object{
metadata: Object.metadata{
labels: {"environment": "dev", "managed": "true"}
}
}

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.

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-labels-jsonpatch
spec:
matchConstraints:
resourceRules:
- apiGroups:
- ''
apiVersions:
- v1
resources:
- pods
operations:
- CREATE
mutations:
- patchType: JSONPatch
jsonPatch:
expression: |
has(object.metadata.labels) ?
[
JSONPatch{
op: "add",
path: "/metadata/labels/managed",
value: "true"
}
] :
[
JSONPatch{
op: "add",
path: "/metadata/labels",
value: {"managed": "true"}
}
]
apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: complex-jsonpatch
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE', 'UPDATE']
mutations:
- patchType: JSONPatch
jsonPatch:
expression: |
[
JSONPatch{
op: "add",
path: "/metadata/labels/environment",
value: "production"
},
JSONPatch{
op: "add",
path: "/metadata/annotations/kyverno.io~1managed",
value: "true"
},
JSONPatch{
op: "replace",
path: "/spec/securityContext/runAsNonRoot",
value: true
}
]
apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: escape-jsonpatch
spec:
matchConstraints:
resourceRules:
- apiGroups:
- ''
apiVersions:
- v1
resources:
- pods
operations:
- CREATE
mutations:
- patchType: JSONPatch
jsonPatch:
expression: |
[
JSONPatch{
op: "add",
path: "/metadata/labels/" + jsonpatch.escapeKey("app.kubernetes.io/name"),
value: "my-app"
}
]

Kyverno supports looping through elements within resources using CEL functions like map(), allowing you to apply mutations to multiple elements within a resource.

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: foreach
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE']
resources: ['pods']
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
spec: Object.spec{
containers: object.spec.containers.map(container, Object.spec.containers{
name: container.name,
securityContext: Object.spec.containers.securityContext{
allowPrivilegeEscalation: false
}
})
}
}

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.

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: foreach-conditional
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE', 'UPDATE']
mutations:
- patchType: JSONPatch
jsonPatch:
expression: |
object.spec.containers.map(c,
c.image.startsWith("nginx:") ?
JSONPatch{
op: "add",
path: "/spec/containers/" + string(object.spec.containers.indexOf(c)) + "/env",
value: [{"name": "NGINX_ENV", "value": "production"}]
} : null
).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.

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.

  1. 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.

  2. 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.

When the trigger and target are the same resource type, the policy will mutate ALL existing resources of that type when a trigger occurs:

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: same-trigger-target
spec:
evaluation:
mutateExisting:
enabled: true
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['namespaces']
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
foo: "bar"
}
}
}

In this example, when any namespace is created or updated, Kyverno will:

  1. Mutate the namespace that triggered the policy
  2. 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.

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.

You have access to properties of the triggering resource via request.object and request.oldObject in variable expressions. In mutation expressions, Object references the target resource from targetMatchConstraints. In order to use request.object or request.oldObject in mutations expressions you have to passing them to the mutation context as a variable first:

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: different-trigger-target
spec:
evaluation:
mutateExisting:
enabled: true
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['namespaces']
resourceNames: ['test-namespace']
targetMatchConstraints:
namespaceSelector:
matchLabels:
test: 'enabled'
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['configmaps']
variables:
triggerNamespace: request.object.metadata.name
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
trigger: variables.triggerNamespace
}
}
}

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 the trigger: test-namespace label

This means if you have 5 ConfigMaps in matching namespaces and update the trigger namespace, all 5 ConfigMaps will be mutated.

As of kyverno 1.17, its possible to select mutation targets with a CEL expression. The expression must use the resource library because it must evaluate to a kubernetes object. The expression can be defined under the targetMatchConstraints.expression field which takes a string CEL expression.

The following policy will target a configmap resource called test-cm-1 in the same namespace as the trigger object.

apiVersion: policies.kyverno.io/v1beta1
kind: MutatingPolicy
metadata:
name: test-mpol-different-trigger-target-expression
spec:
failurePolicy: Fail
evaluation:
mutateExisting:
enabled: true
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE']
resources: ['secrets']
resourceNames: ['trigger-secret']
targetMatchConstraints:
expression: resource.get("v1", "configmaps", object.metadata.namespace, "test-cm-1")
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
mentorship: "kyverno"
}
}
}

The targetMatchConstraints.expression may also reference variables computed from the trigger request, allowing the trigger to determine which resources are resolved as targets.

When mutating existing resources, a policy is evaluated in two phases.

Trigger phase (runs against the resource that triggered the policy):

  1. The trigger is matched against matchConstraints.
  2. Lazy variables bindings are created. Variables are compiled ahead of time, read trigger context such as request.object and request.oldObject, and may perform context-library lookups. Each variable is evaluated at most once — on first reference — and only if referenced.
  3. matchConditions are evaluated against the trigger request. Conditions may reference variables, which evaluate on first reference.
  4. targetMatchConstraints are resolved, using trigger context and variables.

Target phase (runs once per resolved target):

  1. Each resolved target is matched against the targetMatchConstraints resource rules, when defined.
  2. targetMatchConditions are evaluated against the target. Here object refers to the resolved target resource, and variables remain available as the bridge to trigger data.
  3. Mutations are evaluated with Object referring to the target.
  4. The mutation is persisted through the target’s declared API route, including a subresource when one is declared.

Filtering Targets with targetMatchConditions

Section titled “Filtering Targets with targetMatchConditions”

The targetMatchConditions field defines CEL conditions evaluated against each resolved target resource, after variables and target resolution. This allows filtering which targets are mutated, using both target state (object) and trigger data (through variables).

apiVersion: policies.kyverno.io/v1beta1
kind: MutatingPolicy
metadata:
name: filter-targets
spec:
evaluation:
mutateExisting:
enabled: true
matchConstraints:
resourceRules:
- apiGroups: ['apps']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['deployments']
variables:
- name: triggerTeam
expression: request.object.metadata.labels['team']
targetMatchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['UPDATE']
resources: ['configmaps']
targetMatchConditions:
- name: same-team
expression: object.metadata.?labels['team'].orValue('') == variables.triggerTeam
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: >
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
reconciled: "true"
}
}
}

In this example, a Deployment event triggers mutation of only those existing ConfigMaps whose team label matches the trigger’s. Targets that fail a condition are skipped without error.

Target resource rules may declare a subresource. Kyverno resolves the target through its parent resource and persists the mutation through the declared subresource endpoint. This enables use cases such as in-place Pod vertical scaling through the pods/resize subresource (Kubernetes 1.33+), where spec.containers[*].resources on a running Pod can only be changed via /resize:

apiVersion: policies.kyverno.io/v1beta1
kind: MutatingPolicy
metadata:
name: cap-cpu-requests
spec:
evaluation:
admission:
enabled: false
mutateExisting:
enabled: true
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['CREATE', 'UPDATE']
resources: ['pods']
variables:
- name: targetNamespace
expression: request.namespace
- name: targetName
expression: request.name
targetMatchConstraints:
expression: resource.get("v1", "pods", variables.targetNamespace, variables.targetName)
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
operations: ['UPDATE']
resources: ['pods/resize']
targetMatchConditions:
- name: exceeds-cpu-cap
expression: >-
object.spec.containers.exists(c,
quantity(c.resources.?requests['cpu'].orValue('0')).compareTo(quantity('500m')) > 0)
mutations:
- patchType: JSONPatch
jsonPatch:
expression: |-
[
JSONPatch{
op: "replace",
path: "/spec/containers/0/resources/requests/cpu",
value: "500m"
}
]

When this policy processes an existing Pod, the resolved Pod is read through its parent endpoint and the CPU request change is written through pods/resize, which Kubernetes requires for resource changes on running Pods.

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.

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:

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: simple-database-policy
spec:
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE']
mutations:
# First mutation
- patchType: ApplyConfiguration
applyConfiguration:
expression: |
object.spec.containers.exists(c, c.image.contains("cassandra") || c.image.contains("mongo")) ?
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
type: "database"
}
}
} : Object{}
# Second mutation
- patchType: ApplyConfiguration
applyConfiguration:
expression: |
object.metadata.labels.type == "database" ?
Object{
metadata: Object.metadata{
labels: Object.metadata.labels{
backup: "yes"
}
}
} : Object{}

In this example:

  1. First mutation: Checks if any container uses cassandra or mongo images and adds type: database label
  2. Second mutation: Checks if the type: database label exists, then adds backup: yes

The mutations must be ordered from top to bottom in the order of their dependencies within the same policy to ensure consistent results.

The reinvocationPolicy controls whether mutations are reapplied when earlier mutations modify the object:

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: reinvocation-example
spec:
reinvocationPolicy: IfNeeded
matchConstraints:
resourceRules:
- apiGroups: ['']
apiVersions: ['v1']
resources: ['pods']
operations: ['CREATE']
mutations:
- patchType: ApplyConfiguration
applyConfiguration:
expression: |
Object{
metadata: Object.metadata{
labels: {"stage": "initial"}
}
}

Reinvocation Policy Options:

  • Never: Apply mutation once per binding (default)
  • IfNeeded: Re-apply if a previous mutation modifies the object

A MutatingPolicy can mutate JSON objects, not only Kubernetes resources. Set spec.evaluation.mode to JSON and use JSONPatch mutations to modify configuration files, Terraform plans, API payloads, or other JSON data. For JSON-payload evaluation, the input document and mutated result must have an object root. JSON values of any type can be nested inside that object, but top-level arrays, scalars, and null are not supported.

Here is an example of a policy that adds a default region to a document and tags it when it does not already have an owner/team key:

apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
name: add-defaults
spec:
evaluation:
mode: JSON
mutations:
- patchType: JSONPatch
jsonPatch:
expression: |
("region" in object ? [] : [
JSONPatch{op: "add", path: "/region", value: "west"}
]) + ("owner/team" in object ? [] : [
JSONPatch{op: "add", path: "/" + jsonpatch.escapeKey("owner/team"), value: "platform"}
])

To write a policy for JSON payloads:

  1. Set spec.evaluation.mode to JSON.
  2. Optionally, define spec.matchConditions with CEL expressions to select which inputs are mutated.
  3. Define your mutations in spec.mutations using patchType: JSONPatch.

Matching requirements depend on the evaluation path:

Evaluation path spec.matchConstraints spec.matchConditions
CLI JSON payload (--json or jsonPayloads) Not required Optional; filters payloads
CLI Kubernetes resource (--resource) Required to select resources Optional; further filters matches
In-cluster admission Required for normal policy admission Optional; further filters matches

Use the Kyverno CLI to apply JSON mutations:

Terminal window
kyverno apply policy.yaml --json input.json

The mutated object uses the existing YAML mutation-output format on standard output. Use --output-file to write it to a file, or --output-dir to append YAML documents to <policy-name>-mutated.yaml. Output follows normal mutation conventions; there is no batch-wide output transaction across inputs.

When --json is used with both mutating and validating policies, JSON-mode mutations run first, in policy order, and validation then runs on the mutated object. Image-validating and deleting policies also receive the updated object. Like JSON-mode ValidatingPolicies, JSON-mode MutatingPolicies can also be evaluated against Kubernetes resource inputs in the CLI.

Mutations can also be verified with kyverno test using jsonPayloads and patchedResources:

apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: add-defaults
policies:
- policy.yaml
jsonPayloads:
- input.json
results:
- isMutatingPolicy: true
policy: add-defaults
resources:
- input.json
patchedResources: expected.json
result: pass

In a test, resources entries are the exact paths listed under jsonPayloads.

  • Available variables: CEL expressions can use object (the current document), variables, and exceptions, and can create JSONPatch values and call jsonpatch.escapeKey. Admission and cluster-dependent variables and libraries such as request, oldObject, namespaceObject, http, resource, globalContext, and image are not available.
  • Patch semantics: Patches follow RFC 6902. The whole object can be replaced with path: "", but it cannot be removed, and each mutation must produce an object root. If a test operation fails, every patch from that policy is rolled back and the policy is reported as skipped.
  • Numbers: The JSON payload path preserves signed int64 integers exactly, including values above 253; integer literals outside the signed int64 range produce errors. Fractional and exponent-form numbers use IEEE-754 double precision, and patch test compares numbers without conflating adjacent large integers.
  • Ordering and results: Policies are applied in order, and each one sees the output of the previous policy. If any policy errors, no mutated document is produced.
  • Limits: JSON payload documents can be up to 1 MiB, and each JSONPatch mutation expression can produce up to 1024 patch operations.
  • Unsupported settings: ApplyConfiguration patches, mutateExisting, targetMatchConstraints, targetMatchConditions, a reinvocationPolicy other than Never, pod controller autogen, and MutatingAdmissionPolicy generation are rejected in JSON mode. useServerSideApply is not used in JSON mode, which requires JSONPatch mutations.
  • In-cluster lifecycle: JSON-mode MutatingPolicies follow the same lifecycle as JSON-mode ValidatingPolicies. JSON mode does not separately disable policy storage, providers, webhooks, reporting, or normal resource matching. Policies submitted to a cluster still need normal matchConstraints; admission evaluates the JSON expressions against the matched resource object.
  • Exceptions: PolicyExceptions can exempt documents from a JSON-mode policy using matchConditions.

These limits and supported settings reflect the current implementation; see the JSON patcher, JSON policy validation, and CLI resource matching.

While GitOps is great at managing configurations, some changes such as label propagations are best handled using mutating policies.

Kyverno’s MutatingPolicies are designed to interoperate nicely with GitOps solutions.

Mutation changes a resource from the state stored in Git. Configure your GitOps controller to account for those changes so it does not repeatedly undo mutations and reconcile the same resource.

Flux uses server-side apply and dry-run requests to calculate differences. Kyverno processes mutation webhooks during dry runs, so Flux can compare the desired state with mutations included. This usually requires no special configuration.

Dry runs still require policy evaluation. Balance the Flux synchronization interval against the additional admission load.

For Argo CD v2.10 and later, use Server-Side Diff and include mutation webhooks in the comparison:

metadata:
annotations:
argocd.argoproj.io/compare-options: ServerSideDiff=true,IncludeMutationWebhook=true

See Notes for ArgoCD Users and the Argo CD diff documentation.

If server-side diff is unavailable, configure the Application’s ignoreDifferences for fields owned by Kyverno. For example, to ignore a label added to Deployments:

spec:
ignoreDifferences:
- group: apps
kind: Deployment
jqPathExpressions:
- .metadata.labels.foo

Keep exclusions narrow so unrelated configuration drift remains visible.