What's new in Kyverno 1.20
Updated Jim Bugwadia

Kyverno 1.20 runs every policy on the CEL engine. ClusterPolicy and CleanupPolicy no longer execute, so you need to migrate them before you upgrade. The release also verifies images signed with private Sigstore or GitHub attestations, explains each decision from the CLI, adds compensating controls to exceptions and mutates JSON documents such as Terraform plans.
What changes in 1.20 at a glance
| Change | What it means for you | Action |
|---|---|---|
| CEL policy types are the only engine | ClusterPolicy, Policy and CleanupPolicy stop enforcing |
Migrate before you upgrade |
| Private Sigstore and GitHub artifact attestations | Verify images from your own Fulcio and Rekor, or GitHub build provenance | Optional |
| Faster admission | Less CPU and memory per request as policy count grows | None |
kyverno apply --explain |
See which CEL expressions ran and why a resource passed or failed | Optional |
kyverno oci policy bundles |
Version, sign and distribute policy sets as OCI artifacts | Optional |
| Exceptions with expiry and compensating controls | Exemptions record why they exist, when they end and what replaces them | Optional |
JSON mode in MutatingPolicy |
Mutate Terraform plans, API payloads and other JSON with the same CEL | Optional |
Storage version moves to v1 |
Stored policies.kyverno.io objects need rewriting |
Run kyverno migrate after upgrade |
CEL policy types replace ClusterPolicy
Kyverno 1.20 finishes the move that started in 1.14. The legacy kyverno.io engine is gone, and ClusterPolicy, Policy, CleanupPolicy, ClusterCleanupPolicy and the legacy PolicyException no longer run in any controller or in the CLI.
| Legacy type | Replace with |
|---|---|
validate rules |
ValidatingPolicy |
mutate rules |
MutatingPolicy |
generate rules |
GeneratingPolicy |
verifyImages rules |
ImageValidatingPolicy |
CleanupPolicy / ClusterCleanupPolicy |
DeletingPolicy |
kyverno.io PolicyException |
policies.kyverno.io PolicyException |
Each type also has a namespaced variant, such as NamespacedValidatingPolicy, so namespace owners can manage their own policies.
If you upgrade without migrating, here’s what happens:
- Your legacy policies stay in the cluster but show as not ready. The CRD schemas are frozen and protected from Helm prune, so nothing is deleted by accident, and nothing is enforced either.
- Creating or changing a legacy policy is blocked, and
kyverno applyandkyverno testreject legacy manifests. There’s no CLI flag to override this; use a pre-1.20 CLI if you still need to evaluate them. - Results from legacy policies are pruned from policy reports, and queued legacy
UpdateRequestobjects are deleted, not processed. Export any report history you need first.
The legacy CRDs themselves go away in v1.21.
Faster admission as policy count grows
Kyverno 1.20 includes the admission-cost fix that first shipped in 1.19.2. The Kyverno project profiled the CEL engine and found that about 86% of its allocations went into preparing the object for evaluation. That preparation ran once for every matching policy, so a Pod checked against 16 Pod Security Standards policies was parsed 16 times. It now runs once per request.
The project’s engine benchmark, for a ValidatingPolicy and a Pod of about 50 KB:
| Matched policies | Engine time before | Engine time after |
|---|---|---|
| 1 | 0.31 ms | 0.10 ms |
| 16 | 4.91 ms | 0.16 ms |
| 64 | 20.01 ms | 0.36 ms |
Allocations per request fell the same way: at 16 policies, from 37,055 to 2,718. These numbers cover the engine only, not the webhook round trip, the API server or etcd, so they aren’t end-to-end latencies. They come from one machine, and the relative change matters more than the absolute times. At 64 policies, MutatingPolicy time fell from 10.59 ms to 2.97 ms.
1.20 adds its own work for image policies. Compiled CEL programs are reused across admission requests, a cached verification result skips the registry, and matching ImageValidatingPolicy resources run in parallel.
Image verification for private Sigstore and GitHub attestations
ImageValidatingPolicy now verifies images signed against a private Sigstore deployment and GitHub artifact attestations. If you run your own Fulcio and Rekor, or rely on GitHub’s build provenance for private repositories, you can enforce it at admission without workarounds.
The verification path is also stricter:
- Unverified referrers are rejected, and cache entries are bound to the content and attestors they verified.
- Registry credentials for namespaced image policies are scoped to their namespace.
- Policies can match
additionalExtensionsin Fulcio certificates for keyless signing. - Cosign v2 and v3 annotations are both handled, and the newest attestation is the one evaluated.
A CLI built for policy pipelines
kyverno apply --explain prints a CEL decision log that shows which expressions ran and why a resource passed or failed. The admission controller can log the same traces behind a flag, for validating, mutating, generating, deleting and image policies.
kyverno oci packages a set of CEL policies as an OCI bundle, so you can version, sign and distribute policy like any other artifact. New subcommands validate, inspect and resolve bundles, and kyverno test and kyverno apply run against them directly.
kyverno test now supports mocks, apiCall and context, so a policy that looks up live data can be tested offline in CI. Tests also fail correctly on messageExpression errors. Smaller additions include --output-dir and --output-file for apply, and JSON or YAML output for kyverno version.
Exceptions that carry their justification
A PolicyException can now set an expiration date and metadata, so a report shows when an exemption lapses and why it exists.
Exceptions can also define compensating controls: CEL checks in spec.validations that a resource must pass before the exception applies. For example, you could exempt a workload from a privilege rule only while it runs in a dedicated namespace. Compensating controls apply only to ValidatingPolicy and NamespacedValidatingPolicy; other policy types ignore them.
Policy for JSON beyond Kubernetes
MutatingPolicy gains a JSON mode, so the CEL policies that set defaults on Kubernetes resources can also change Terraform plans, API payloads and other JSON input. The document must have an object at its root. Nested arrays work; top-level arrays and scalars don’t.
GeneratingPolicy also accepts inline YAML templates, so a policy that creates a resource reads like that resource.
Other changes worth knowing
- Global context projections gain a CEL
expressionfield, and JMESPath projections are deprecated. - Autogen extends to custom workload CRDs such as JobSet, and can be turned on or off per Pod controller.
- The Pod Security Standards policies in the Helm chart support Pod user namespaces, stop denying Windows Pods, add a
disallow-host-probesBaseline policy and let you choose which Pod Security Admission version to track. - 1.20 includes the security hardening from Kyverno 1.19.2, such as bounded CEL evaluation cost and bounded registry reads.
- The Helm chart adds lifecycle hooks for every controller, and the Grafana dashboard covers the
policies.kyverno.iotypes.
How to upgrade to Kyverno 1.20
Migrate your legacy policies before you upgrade.
-
Migrate. Use the CEL policy migration guide and our walkthrough of Kyverno policy migration to CEL-based policies. Run your existing CLI tests against the migrated policies, or try them in the Kyverno Playground.
-
Upgrade with Helm. If no legacy policies remain, upgrade as usual. If any exist, the chart stops and lists them. Once you accept that they won’t be enforced, run:
helm upgrade kyverno kyverno/kyverno -n kyverno \ --set upgrade.acknowledgeLegacyPoliciesNotEnforced=true -
Rewrite stored objects. The storage version for
policies.kyverno.iotypes moves fromv1beta1tov1. Runkyverno migratefor each type, for example:kyverno migrate --resource validatingpolicies.policies.kyverno.io
The acknowledgement doesn’t migrate or delete anything, so a downgrade stays possible. With plain YAML manifests, nothing blocks the install; legacy policies are marked NotReady and Kyverno logs warnings. If you uninstall and reinstall, back up legacy policies first, because deleting a CRD deletes its objects.
Kyverno 1.20 supports Kubernetes v1.34 to v1.37. Read the upgrade guide and the v1.20.0 release notes before you start.
Where Kyverno goes next
With one engine behind every policy type, the project’s roadmap is about running that engine in more places and making policy easier to write. These are directions the maintainers are working toward, not release commitments:
- Schema-based JSON, using JSON Schema and gRPC Protobuf to describe non-Kubernetes inputs to CEL.
- A unified CLI,
kyverno serve, available as a web service, a container and an SDK. - Cedar support for conditional authorization.
- A Rust SDK for evaluating Kyverno policies inside Rust applications.
- Plugins, Skills and MCP for writing and testing policies with AI assistants.
The next release, v1.21, removes the legacy policy CRDs entirely, so 1.20 is the time to finish your migration.
Get help with the upgrade
If you run Kyverno in production and want a second pair of eyes before you upgrade, sign up for a free Production Kyverno Assessment, a one-hour expert review of security hardening, performance tuning and troubleshooting. If you need to stay on an older release while you migrate, Nirmata Enterprise for Kyverno backports CVE fixes to every supported release, with 24/7 support and SLAs. To get started with the open source project, see Kyverno open source.
Frequently asked questions
Do ClusterPolicy resources still work in Kyverno 1.20?
No. Kyverno 1.20 removes the legacy engine, so ClusterPolicy, Policy, CleanupPolicy, ClusterCleanupPolicy and the legacy kyverno.io PolicyException no longer execute in any controller or in the CLI. Existing objects stay in the cluster, frozen and marked not ready, so you can export them or downgrade. Migrate them to the CEL policy types before you upgrade.
How do I upgrade to Kyverno 1.20 if legacy policies still exist?
The Helm chart blocks the upgrade and lists the legacy resources it found. Migrate them first. If you accept that they will no longer be enforced, set upgrade.acknowledgeLegacyPoliciesNotEnforced=true. After the upgrade, run kyverno migrate to rewrite stored policies.kyverno.io objects from v1beta1 to v1. Export legacy report history first, because legacy report results are removed.
Is Kyverno 1.20 faster than earlier releases?
For CEL policies, yes. Kyverno 1.20 includes the admission-cost fix first shipped in 1.19.2, which prepares each object once per request instead of once per matching policy. The Kyverno project measured engine time for 16 matching policies falling from 4.91 ms to 0.16 ms. Image policies in 1.20 also reuse compiled programs, skip the registry on cache hits and run in parallel.
Which Kubernetes versions does Kyverno 1.20 support?
Kyverno 1.20 supports Kubernetes v1.34 to v1.37. It is built with Go 1.27.3 and the Kubernetes v0.37 libraries. Before you upgrade, check the Kyverno upgrade guide and the full v1.20.0 release notes, and migrate any legacy policies, because the release no longer enforces ClusterPolicy or CleanupPolicy resources.