Kubernetes Policy as Code: Kyverno vs OPA Gatekeeper (2026)

Kubernetes Policy as Code: Kyverno vs OPA Gatekeeper (2026)

Kubernetes Policy as Code: Kyverno vs OPA Gatekeeper (2026)

Every platform team eventually hits the same wall. A developer ships a Deployment that runs as root, mounts the host filesystem, pulls :latest from an unsigned registry, and asks for no resource limits. The cluster happily schedules it. Nobody notices until an incident review. Kubernetes policy as code is the discipline that stops that Deployment at the door — codifying the rules your organization has always meant to enforce as versioned, testable objects that live next to your manifests instead of in a wiki nobody reads.

Two engines dominate this space, and in 2026 the choice between them is sharper than it has ever been. Kyverno graduated within the Cloud Native Computing Foundation in March 2026, and its YAML-native model has been eating adoption share. OPA Gatekeeper, built on the long-graduated Open Policy Agent, remains the choice for teams with Rego investments spanning far beyond Kubernetes. Meanwhile the API server itself grew native policy: ValidatingAdmissionPolicy went GA, and MutatingAdmissionPolicy joined it. That changes the calculus for everyone.

What this covers: the admission mechanism both engines hook into, Kyverno’s YAML versus OPA’s Rego, the full capability surface (validate, mutate, generate, image verification, cleanup, exceptions, reporting), performance and webhook failure modes, multi-tenancy fit, CNCF status, a real decision matrix, and when the in-tree CEL policies replace either engine.

Context and Background

Kubernetes has always shipped an extension point for this: dynamic admission control. When any object is created, updated, or deleted through the API server, the request passes through a chain of admission plugins before it is persisted to etcd. Two of those plugins — MutatingAdmissionWebhook and ValidatingAdmissionWebhook — call out to registered HTTP endpoints, hand them the incoming object as an AdmissionReview, and let them respond with an allow/deny verdict or a JSON patch. That callout is the seam every policy engine lives in.

The Open Policy Agent project pioneered general-purpose policy as code, and OPA Gatekeeper packaged it specifically for Kubernetes admission control, adding a constraint framework, CRDs, and audit. OPA reached CNCF graduated status back in January 2021. Kyverno arrived later with a deliberately different bet: policies should be Kubernetes resources written in YAML, not a separate language. That bet paid off — CNCF announced Kyverno’s graduation in March 2026, placing it in the same top tier as Kubernetes, Prometheus, and Envoy.

The third force is the API server itself. Community frustration with webhook fragility produced KEP-3488, which put Common Expression Language (CEL) rules directly inside kube-apiserver. ValidatingAdmissionPolicy graduated to GA in Kubernetes v1.30. Its mutation counterpart, MutatingAdmissionPolicy, reached GA in v1.36. These in-tree policies need no external pod, no webhook, and no network hop — reshaping when you actually need Kyverno or Gatekeeper at all. For teams already running multi-tenant platforms, policy sits alongside strong workload isolation and namespace-per-tenant design as a load-bearing control. The Kubernetes admission controllers reference documents the full plugin chain.

How Admission Control Actually Works

Policy as code on Kubernetes is only as good as your understanding of the admission pipeline. Every enforcement decision — Kyverno’s, Gatekeeper’s, or the API server’s own CEL rules — happens inside one narrow window between “request authenticated and authorized” and “object written to etcd.” Get the mechanics wrong and you build policies that either miss objects or take the cluster down.

Here is the direct answer: Kubernetes admission control runs in two ordered phases. The mutating phase runs first and may rewrite the incoming object; the validating phase runs second and may only accept or reject it. External engines register webhooks that the API server calls over TLS; the newer in-tree policies run CEL expressions inside the API server with no callout. Both Kyverno and OPA Gatekeeper are, at bottom, webhook servers — sophisticated ones, but webhook servers.

Sequence diagram of the Kubernetes admission request flow for policy as code

Figure 1: The admission request flow — the API server runs the mutating phase, then the validating phase, delegating to the policy engine at each step before persisting or rejecting.

When you kubectl apply a Pod, the API server authenticates you, checks RBAC, then decodes the object. It calls every matching mutating webhook in sequence, re-invoking them until the object stops changing (a reinvocation policy exists precisely to handle policies that react to each other’s patches). Only then does it run the validating webhooks, which see the fully mutated object and vote. A single deny anywhere in the validating set rejects the whole request. If everything passes, the object is written and the client gets its 201 Created.

The two phases are not symmetric

Ordering matters more than people expect. Because mutation runs first, a policy that injects a default securityContext must be a mutating policy; a policy that rejects privileged containers must be validating. You cannot enforce a value in the validating phase and also set it — you get one or the other per webhook. Well-designed policy sets pair a mutating default with a validating guard: mutate to inject runAsNonRoot: true, then validate that nobody overrode it back to false in a later step.

Webhooks add a network hop; CEL does not

An external engine means the API server makes a TLS call to a pod, waits for a response, and blocks the request until it returns or times out. That hop is the source of nearly every production incident involving policy engines. The in-tree CEL policies eliminate it entirely: the expression is compiled and evaluated in-process. This is why ValidatingAdmissionPolicy is not just a convenience — it is a fundamentally different reliability profile, because there is no external dependency in the critical path of every write to your cluster.

Match conditions decide the blast radius

Both engines and the in-tree policies use match rules — by group, version, kind, namespace selector, and label selector — to decide which requests they even see. Loose matches (every resource, every namespace) multiply the cost of every policy and widen the failure surface. Tight matches keep the engine off the hot path for objects it does not care about. This single design choice, more than raw engine speed, governs how much latency your policy layer adds.

Kyverno vs OPA Gatekeeper: Architecture and Language Model

The deepest difference between the two engines is not a feature — it is the authoring model, and it colors everything downstream. This is the heart of the kyverno vs opa gatekeeper decision, so it deserves the most attention.

Architecture comparison of Kyverno OPA Gatekeeper and in-tree CEL policy as code

Figure 2: Three paths into the admission chain — in-tree CEL policies inside the API server, Kyverno’s YAML-plus-CEL controllers, and Gatekeeper’s Rego constraint framework.

Kyverno models policies as native Kubernetes resources. A ClusterPolicy or Policy object contains rules, each with a match block and one action: validate, mutate, generate, or verifyImages. There is no separate language runtime to learn — if you can read a Kubernetes manifest, you can read a Kyverno policy. Pattern matching uses an overlay syntax where you write the shape you want, using operators like !*, >, and anchors (+, =, X()) to express conditions. As of Kyverno 1.17, released February 2026, CEL-based policy types (ValidatingPolicy, MutatingPolicy, GeneratingPolicy, ImageValidatingPolicy) were promoted to v1 GA, and the legacy ClusterPolicy API was deprecated with removal targeted for a later release. Kyverno now speaks both its original overlay syntax and CEL.

OPA Gatekeeper takes the opposite approach. Policies are written in Rego, OPA’s declarative query language. You author a ConstraintTemplate (the reusable logic, containing Rego) and then instantiate it with one or more Constraint objects (the parameters — which namespaces, which allowed values). Rego is genuinely powerful: it handles set logic, aggregation across multiple objects, and complex joins that are awkward or impossible in Kyverno’s overlay model. But Rego is a real language with a learning curve, and most teams have exactly one person who is fluent.

YAML lowers the floor; Rego raises the ceiling

The practical consequence is a trade-off between accessibility and expressiveness. Kyverno’s YAML means a wider set of engineers can write and review policies without specialized training — policies read like the manifests they govern. Gatekeeper’s Rego means a smaller group can express richer logic, and — critically — reuse that logic outside Kubernetes. The same Rego drives OPA policies for Envoy authorization, Terraform plan checks, and CI gates. If your organization has standardized on OPA across the stack, Gatekeeper is the natural Kubernetes endpoint of that investment.

Both now embrace CEL, but for different reasons

CEL is the connective tissue of modern Kubernetes policy. Gatekeeper can generate and hand off ValidatingAdmissionPolicy objects, letting simple constraints run in-tree while complex ones stay in Rego. Kyverno’s CEL policy types let you write expressions in the same dialect the API server uses, easing eventual migration to in-tree enforcement. Both projects read the same signal: the API server is becoming the enforcement engine for the common case, and the add-on engines are repositioning around what CEL cannot yet do — mutation chains, resource generation, image verification, and cross-object logic.

A Kyverno policy reads like the object it governs

Here is a Kyverno policy that requires every Pod to set a non-root security context — the canonical baseline control:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-run-as-nonroot
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: check-runasnonroot
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Containers must set runAsNonRoot to true."
        pattern:
          spec:
            =(securityContext):
              =(runAsNonRoot): true
            containers:
              - =(securityContext):
                  =(runAsNonRoot): true

The =() anchors mean “if this field exists, it must match.” validationFailureAction: Enforce blocks non-compliant Pods; switch it to Audit and violations only surface in reports. The overlay is the object shape — no imperative code.

A Gatekeeper constraint splits logic from parameters

The equivalent in Gatekeeper is two objects. First the template with Rego:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequirednonroot
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredNonRoot
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequirednonroot
        violation[{"msg": msg}] {
          c := input.review.object.spec.containers[_]
          not c.securityContext.runAsNonRoot == true
          msg := sprintf("container %v must set runAsNonRoot", [c.name])
        }

Then the constraint that applies it:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredNonRoot
metadata:
  name: pods-must-run-nonroot
spec:
  enforcementAction: deny
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]

Same outcome, two very different mental models. The Gatekeeper split is more ceremony for one rule but pays off when one template serves dozens of parameterized constraints.

Capabilities Compared: Beyond Just Saying No

Validation — rejecting bad objects — is table stakes. The real separation between engines is everything that happens around it: reshaping objects, spawning new ones, verifying supply-chain provenance, and reporting what the cluster looks like. This is where Kyverno’s scope is materially wider than Gatekeeper’s.

Kyverno policy as code capability lifecycle from validate to reporting

Figure 3: The capability surface — validate, mutate, generate, and image verification feed enforcement, while background scanning and admission both populate the PolicyReport CRD, with cleanup and exceptions handling lifecycle.

Validate both engines do well. Mutate is native to Kyverno and, until recently, absent from Gatekeeper’s admission enforcement (Gatekeeper’s mutation exists but is a separate, less mature subsystem than its validation). Kyverno mutation injects defaults, adds labels, patches sidecars, and rewrites images — and it can mutate existing resources, not just incoming ones.

Generate is a Kyverno signature capability with no Gatekeeper equivalent. A single policy can create a default NetworkPolicy, ResourceQuota, or image-pull Secret in every new namespace, and keep those generated objects in sync if the source changes. For platform teams standing up tenant namespaces, this replaces a pile of controllers with one policy.

Image verification is a supply-chain control, not a nicety

verifyImages is where Kyverno pulls decisively ahead for security teams. Kyverno verifies container image signatures at admission time using Cosign or Notary, supporting keyed and keyless (Fulcio/Rekor) signatures, attestation checks, and registry-specific attestors. An unsigned or tampered image is rejected before it can run. Gatekeeper has no first-class image-signature verification; teams pairing it with signing typically bolt on a separate admission controller. If you are building toward SLSA-aligned supply-chain guarantees, this is a genuine differentiator.

Cleanup, exceptions, and reporting round out the lifecycle

Kyverno’s cleanup policies delete resources on a TTL or schedule — stale Jobs, orphaned PVCs, expired test namespaces — which no other admission engine does natively. PolicyException resources let a specific workload bypass a policy without weakening the policy itself, which is essential at multi-tenant scale where blanket rules meet legitimate edge cases. Both engines emit the PolicyReport CRD (the open wgpolicyk8s.io standard): namespaced PolicyReport and cluster-scoped ClusterPolicyReport objects that record pass/fail results per resource, queryable with plain kubectl. The Kyverno reports documentation details the schema, and the OPA Gatekeeper documentation covers Gatekeeper’s audit and constraint-status equivalents.

Background Scanning and Audit

Admission control only sees objects at the moment they change. But most clusters are full of resources that predate any given policy — Deployments created long before you decided root containers were forbidden. This is what background scanning solves, and it is a first-class concern for compliance, not an afterthought.

Kyverno’s background scanning periodically re-evaluates existing resources against every validate and verifyImages rule, recording violations in PolicyReport/ClusterPolicyReport objects without blocking anything. It is enabled per policy via spec.background: true. This gives you a continuously updated inventory of drift — every object that would fail today’s policy, whether or not it passed when it was created. Gatekeeper’s audit controller does the analogous job, periodically evaluating stored objects against constraints and writing results into each constraint’s status.violations.

The design implication is subtle but important: background scanning lets you deploy a policy in audit/report mode, measure the blast radius across your entire existing fleet, and only then flip to enforce. Skipping this step is how teams take down production — they set Enforce on a policy that would reject 40% of already-running workloads on their next rollout. Report first, quantify, remediate, then enforce.

One operational caveat worth naming: background scanning generates report objects proportional to your resource count times your policy count, and on large clusters this has itself been a source of etcd and controller load. Both projects have tuned report aggregation over time, but on a fleet with tens of thousands of objects, the reporting layer is a capacity-planning item, not a free feature. Budget for it the way you budget for metrics cardinality.

Scan cadence is also a knob, not a constant. A scan interval that is too tight re-evaluates the whole fleet more often than compliance needs and burns controller CPU; too loose and drift lingers unreported between passes. The right cadence tracks how fast your cluster changes and how quickly your audit obligations require you to notice a violation — hourly is common for compliance-heavy environments, longer for stable internal platforms. Feed the resulting reports into the same dashboards as your admission denials so the two views — what got blocked at the door and what is already inside failing today — sit side by side. That combined picture, not either signal alone, is what tells you whether a policy is safe to move from audit to enforce.

Performance, Latency, and Webhook Failure Modes

This is the section that decides whether your policy layer is an asset or an outage waiting to happen. Because both engines sit in the synchronous admission path, their availability is your cluster’s write availability for the objects they match. A slow or dead webhook does not just fail policy — it can fail every create and update in its match scope.

The direct answer on performance: in-tree CEL policies add negligible latency because they run in-process; external webhook engines add a network round trip plus evaluation time, typically single-digit to low-tens of milliseconds per matched request under healthy conditions, but with a long tail under load, pod restarts, or network blips. Kyverno and Gatekeeper are both fast enough for most clusters; the risk is not steady-state latency but failure behavior.

failurePolicy is the single most consequential setting

Every webhook configuration carries a failurePolicy: Fail or Ignore. With Fail, if the webhook is unreachable or times out, the API server rejects the request — fail-closed, secure, but capable of bricking your cluster if the policy pods are down. With Ignore, unreachable webhooks are skipped — fail-open, available, but a security gap during outages. There is no universally correct answer. Security-critical policies (block privileged, verify images) often warrant Fail; convenience mutations often warrant Ignore. The catastrophic anti-pattern is Fail on a webhook that matches namespaces including the one the policy engine runs in — a pod restart then cannot reschedule because admission for its own namespace is failing closed.

timeoutSeconds bounds the damage

The webhook timeoutSeconds (max 30, default 10) caps how long the API server waits. Set it too high and a hung engine stalls every matched request for that long; set it too low and legitimate slow evaluations get cut off. Values in the 3–10 second range are typical, tuned to observed p99 evaluation time with headroom. Pair a tight timeout with Ignore for non-critical policies so a slow engine degrades gracefully rather than blocking.

Availability engineering is mandatory, not optional

Because the engine is in the write path, treat it like critical infrastructure: run multiple replicas across nodes and zones, set a PodDisruptionBudget, exclude the kube-system and the engine’s own namespace from its match rules, and monitor webhook latency and error rate as first-class SLIs. The in-tree CEL policies sidestep this entire class of problem, which is precisely their appeal — no pod to keep alive, no webhook to time out, no failurePolicy dilemma. That reliability delta is the strongest argument for pushing every policy you can express in CEL down into the API server.

Multi-Tenancy Fit and Ecosystem Status

Policy as code and multi-tenancy are deeply intertwined: the whole point of enforcing rules is that you do not trust every workload equally. How each engine handles namespace-scoped rules, exceptions, and delegation determines its fit for platforms serving many teams.

Kyverno supports namespaced Policy objects alongside cluster-wide ClusterPolicy, letting a platform team set cluster guardrails while tenant admins manage rules inside their own namespaces. Its PolicyException mechanism is purpose-built for the multi-tenant reality where a blanket rule needs targeted, auditable exemptions. Generate policies auto-provision per-namespace defaults — NetworkPolicies, quotas, RBAC — which is exactly the bootstrapping burden multi-tenant platforms carry. This pairs naturally with multi-cluster fleet management via Cluster API, where policy must be applied consistently across many clusters from one control plane.

Gatekeeper’s constraint framework scopes cleanly by namespace and label selector, and its parameterized templates suit an organization that wants a small central team to author reusable logic and hand tenants tightly bounded knobs. Its strength in multi-tenancy is governance discipline: the template/constraint split enforces a separation between who writes policy logic and who applies it.

CNCF status and adoption trajectory

On maturity, both projects are CNCF graduated. OPA graduated in January 2021; Gatekeeper is the Kubernetes-focused subproject of that graduated OPA project, with its latest release line at v3.22.x as of early 2026. Kyverno graduated in March 2026. In practical terms, both are safe long-term bets — graduation signals production maturity, security process, and governance. Multiple 2026 community surveys report Kyverno overtaking Gatekeeper by adoption, driven largely by the lower authoring barrier of YAML and the breadth of mutate/generate/verifyImages. But Gatekeeper remains entrenched wherever Rego is already a cross-cutting standard, and that entrenchment is sticky because the Rego assets have value far beyond the cluster.

The Decision Matrix

The following matrix scores each engine against the criteria that actually drive the choice. In-tree CEL policies are included because for a growing share of use cases they are the right answer — no add-on required.

Criterion Kyverno OPA Gatekeeper In-tree CEL (VAP/MAP)
Policy language YAML overlay + CEL Rego CEL only
Learning curve Low High (Rego) Medium (CEL)
Validate Yes Yes Yes
Mutate Yes, mature Limited/separate subsystem Yes (MutatingAdmissionPolicy, GA)
Generate resources Yes No No
Image verification (cosign/notary) Yes, native No first-class No
Cleanup / TTL policies Yes No No
Policy exceptions Yes (PolicyException) Yes (via config) No native equivalent
Reporting PolicyReport CRD Audit status + PolicyReport kubectl/metrics only
Reuse outside Kubernetes No Yes (Envoy, Terraform, CI) No
Runs without external pod No No Yes (in kube-apiserver)
Failure-mode risk Webhook (failurePolicy) Webhook (failurePolicy) None — no callout
CNCF status Graduated (2026) Graduated (via OPA, 2021) Core Kubernetes
Best for Broad platform policy Rego-standardized orgs Simple in-tree enforcement

Read the matrix as a layering strategy, not a single winner. Most mature 2026 platforms run a hybrid: in-tree CEL for the simple validations, and one add-on engine for the capabilities CEL lacks. The add-on choice comes down to language preference and whether you need mutation, generation, and image verification.

When In-Tree CEL Replaces Either Engine

The most important architectural shift of the last few Kubernetes releases is that the API server can now enforce policy itself. This changes the default answer to “which policy engine?” for a meaningful slice of use cases, so it deserves an explicit decision rather than a footnote.

Decision flow for choosing Kyverno OPA Gatekeeper or in-tree CEL policy as code

Figure 4: A decision path — simple validation goes to in-tree CEL, mutation/generation/image needs point to Kyverno, and cross-stack Rego reuse points to Gatekeeper.

A ValidatingAdmissionPolicy is three objects working together: the ValidatingAdmissionPolicy (the CEL rules and their match criteria), an optional parameter resource it reads (so one policy can be tuned per namespace, like a Gatekeeper constraint), and a ValidatingAdmissionPolicyBinding that connects the policy to a scope and sets the enforcement action. CEL expressions get access to object, oldObject, request, and named variables, which is enough to express the large majority of validation rules teams actually write — required labels, forbidden hostPath mounts, allowed registries, replica floors, resource-limit requirements.

The migration heuristic

Push a policy in-tree when three things are true: it is pure validation or simple mutation, it can be expressed in CEL without cross-object lookups, and its reliability matters enough that removing the webhook hop is worth losing rich reporting. Keep a policy in Kyverno or Gatekeeper when it generates resources, verifies image signatures, runs cleanup, needs multi-object aggregation, or depends on the PolicyReport ecosystem your compliance tooling already consumes. Gatekeeper even automates half of this: it can render eligible constraints as ValidatingAdmissionPolicy objects, so simple rules run in-tree while complex Rego stays in the webhook.

What CEL still cannot do

The gap is real. In-tree CEL has no equivalent of Kyverno’s generate (spawning NetworkPolicies or quotas), no cosign/notary image verification, no scheduled cleanup, and no built-in report CRD — you observe it through API-server metrics and admission responses, not a queryable inventory. Complex logic that spans multiple objects, or reads other cluster state, is also out of scope by design, since the expression only sees the request in front of it. Those constraints are exactly why the add-on engines are not going away; they are repositioning around the work the API server cannot yet do.

Trade-offs, Gotchas, and What Goes Wrong

No policy engine is free, and the failure stories cluster around a few predictable mistakes. The first and most damaging is fail-closed self-lockout: setting failurePolicy: Fail on a webhook whose match rules include the policy engine’s own namespace or kube-system. When the engine restarts, admission for its dependencies fails closed, nothing can schedule, and you are recovering a cluster by hand. Always exclude system namespaces and the engine’s namespace from enforcement.

The second is enforcing before measuring. A policy set to Enforce/deny on day one will reject every non-compliant object on its next reconcile, and in a real cluster that is a large fraction of workloads. Background scanning and audit exist precisely so you can quantify the blast radius first. Skipping that step turns a governance rollout into an incident.

The third is Rego drift in Gatekeeper. Because Rego is expressive, teams write clever policies that a single author understands and nobody else can safely modify. When that author leaves, the policy becomes untouchable load-bearing code. Kyverno’s YAML is less powerful but far more reviewable — a real operational virtue when policies outlive their authors.

The fourth is reporting overload. On large fleets, PolicyReport and audit objects scale with resources times policies and can pressure etcd and controllers. Treat report volume as a capacity concern; scope policies tightly and tune report retention.

The fifth is assuming in-tree CEL is a drop-in replacement. It validates and (as of v1.36) mutates, but it does not generate resources, verify image signatures, run cleanup, or provide rich reporting. Migrating a Kyverno-heavy platform to pure CEL loses real capability. CEL replaces the simple cases, not the whole engine.

Practical Recommendations

Start by deciding what actually belongs in-tree. Any policy that is pure validation on an incoming object — required labels, forbidden fields, allowed registries — should be a ValidatingAdmissionPolicy running inside the API server. It has the best reliability profile and no operational overhead. Push everything you can down to CEL.

For the capabilities CEL cannot cover, pick one add-on engine and standardize. Choose Kyverno if your team writes YAML fluently, you need mutation/generation/image-verification, and you want the widest set of engineers able to author and review policy. Choose OPA Gatekeeper if your organization has already standardized on Rego across Envoy, Terraform, and CI, and you value reusing that logic more than lowering the authoring barrier.

Whichever you pick, follow this checklist:

  • Roll out in audit/report mode first. Use background scanning or audit to measure the blast radius before flipping to enforce.
  • Exclude system namespaces and the engine’s own namespace from enforcement to avoid self-lockout.
  • Set failurePolicy deliberately per policyFail for security-critical rules, Ignore for conveniences — and tune timeoutSeconds to observed p99.
  • Run the engine HA across zones with a PodDisruptionBudget, and monitor webhook latency and error rate as SLIs.
  • Version policies in Git and test them in CI (both engines ship CLI test tooling) before they reach a cluster.
  • Prefer CEL variants for anything you might later migrate in-tree.

Treat the policy layer as production infrastructure. It sits in the write path of your entire cluster; it deserves the same rigor as your ingress or your CNI. Observability on top of policy events — routed through a unified OpenTelemetry pipeline — turns denials and audit drift into signals you can alert on rather than surprises.

Frequently Asked Questions

Is Kyverno or OPA Gatekeeper better for Kubernetes policy as code?

Neither is universally better. Kyverno wins on breadth and accessibility — YAML-native policies, mutation, resource generation, and native image verification make it the stronger default for most platform teams. OPA Gatekeeper wins when your organization has standardized on Rego across Envoy, Terraform, and CI, because it reuses that policy logic at the Kubernetes layer. In 2026, adoption surveys favor Kyverno, but Gatekeeper remains a solid choice for Rego-invested shops. Both are CNCF graduated and production-ready.

Does ValidatingAdmissionPolicy replace Kyverno and Gatekeeper?

Not entirely. ValidatingAdmissionPolicy (GA in Kubernetes v1.30) and MutatingAdmissionPolicy (GA in v1.36) run CEL inside the API server with no webhook, giving them the best reliability profile. They replace the simple validation and mutation cases. But they do not generate resources, verify image signatures with cosign, run cleanup policies, or emit rich PolicyReports. For those capabilities you still need Kyverno or Gatekeeper. The 2026 pattern is hybrid: in-tree CEL for the common case, an add-on engine for the rest.

What is the difference between Kyverno’s YAML and OPA’s Rego?

Kyverno policies are Kubernet

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *