2026
Kyverno: Policy Enforcement for Kubernetes
A cluster will accept almost anything you submit to it. Kyverno sits at the admission layer and changes that. Policies written as plain Kubernetes resources validate, mutate and generate objects before they are stored, keeping configuration drift from ever reaching etcd.
A Kubernetes cluster is easy to misconfigure. A developer pushes a deployment with no resource limits and a noisy pod starts consuming memory that neighbouring workloads needed. Someone pulls from a public registry without pinning a digest and a changed image tag silently breaks production. A new namespace gets created without the NetworkPolicy that should have been there from the start.
These are not exotic failure modes. They happen in most clusters eventually and they share a common cause: the cluster accepted resources it should have questioned. Kubernetes has no built-in mechanism to enforce organisational standards at admission time beyond its own schema validation. That gap is exactly what Kyverno fills.
Kyverno is a policy engine built specifically for Kubernetes. It runs as an admission controller inside the cluster and intercepts every resource before it is stored. Policies are written as Kubernetes resources themselves, which means there is no separate policy language to learn. You define what you want using the same YAML structure you already use for everything else and Kyverno takes care of evaluating it on every applicable request.
How Kyverno intercepts requests
Kyverno works through Kubernetes admission webhooks. When you install it, two webhook configurations are registered with the API server: one for mutation and one for validation. From that point forward, any request to create or update a resource that matches a policy gets routed through Kyverno before the API server decides whether to accept it.
The order matters. Mutation runs before validation. A mutate rule can add a missing field, then a validate rule can require that field to be present. Together they let you combine sensible defaults with hard requirements: inject a resource limit if none is set, then reject any resource where the limit is still absent after mutation has had its chance to add one.
Request arrives at the API server
A user or controller submits a create, update or delete request. The API server authenticates it and begins processing
Mutating webhooks run
Kyverno's mutating webhook fires first. Any mutate rules that match the resource are applied. The modified object continues through the pipeline
Object is validated by the API server
Kubernetes validates the (now potentially mutated) object against its own schema
Validating webhooks run
Kyverno's validating webhook fires. All matching validate rules are evaluated. If any rule fails, the request is rejected with the configured message
Object is stored in etcd
Only resources that passed every check reach persistent storage. Generate rules then fire in the background to create any dependent resources
Rule types
A Kyverno policy is made up of one or more rules. Each rule has a type that determines what it does when it matches a resource. The four types cover the main things you want to do with policy: block bad configurations, fix missing ones, create dependent resources automatically and verify image provenance.
validate
Checks whether a resource meets a condition. If the check fails, Kubernetes rejects the request and returns an error to whoever submitted it. Validation rules are the most common type. They enforce things like required labels, forbidden image registries and minimum resource limits.
mutate
Modifies a resource before it is stored. If a pod is submitted without a memory limit, a mutate rule can add one automatically. If a deployment is missing a standard label, mutation can inject it. The user submitting the resource never sees the change.
generate
Creates additional resources in response to an event. When a new namespace appears, a generate rule can automatically create a NetworkPolicy or a default ResourceQuota inside it. This is useful for enforcing baseline configuration that every namespace should have.
verifyImages
Checks container image signatures before a pod is allowed to run. Works with tools like Cosign. If an image was not signed by a trusted key, the pod is rejected. This closes the gap between building an image in CI and knowing it has not been tampered with before it runs in production.
ClusterPolicy and Policy
Kyverno ships two custom resource kinds for writing policies. The difference between them is scope. One applies across the entire cluster, the other is namespaced. Choosing between them is mostly a question of who owns the policy and how broadly it should apply.
ClusterPolicy
Cluster-wideApplies rules across all namespaces. Use this for baseline security controls that should be consistent everywhere, like blocking privileged containers or requiring image digests.
Policy
NamespacedApplies rules only within the namespace where it is created. Useful when different teams have different requirements or when you want to give namespace owners the ability to define their own constraints.
What policies look like
Policies are plain Kubernetes manifests. You apply them with kubectl apply just like any other resource. The match block narrows which resources the rule applies to. The rule body describes what to check, change or create. There is no separate CLI tool required to deploy them.
Validate — require resource limits on all containers
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
spec:
validationFailureAction: Enforce
rules:
- name: check-limits
match:
resources:
kinds:
- Pod
validate:
message: "All containers must define resource limits"
pattern:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"
Mutate — add a default label to pods that are missing it
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-labels
spec:
rules:
- name: add-team-label
match:
resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
+(managed-by): kyverno
Generate — create a default NetworkPolicy in every new namespace
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: default-network-policy
spec:
rules:
- name: generate-network-policy
match:
resources:
kinds:
- Namespace
generate:
kind: NetworkPolicy
name: default-deny-ingress
namespace: {{request.object.metadata.name}}
data:
spec:
podSelector: {}
policyTypes:
- Ingress
The +(field) syntax in the mutate example is a conditional patch anchor. It adds the field only when it does not already exist. Without it, the mutation would overwrite values that were deliberately set by the submitter, which is usually not what you want from a default-injection rule.
Audit and Enforce modes
Every validate policy has a validationFailureAction field that controls what happens when a resource fails the check. Setting it to Enforce rejects the request. Setting it to Audit allows the request through but records a policy report showing that the resource violated the rule.
This distinction is genuinely useful when rolling out new policies. Switching directly to Enforce on a cluster with existing workloads risks blocking legitimate deployments the moment a CI pipeline runs. Starting in Audit mode lets you see which resources would have been rejected, fix the violations at your own pace and switch to Enforce only once you are confident that nothing legitimate will be blocked. The policy reports Kyverno generates are standard Kubernetes resources you can query with kubectl.
Checking policy reports
kubectl get policyreport -A
Lists all namespaced policy reports. Each row shows how many results pass or fail.
kubectl get clusterpolicyreport
Cluster-scoped resources like namespaces appear in ClusterPolicyReports instead.
kubectl describe policyreport -n my-namespace
Shows the full detail: which policy failed, which resource triggered it and the message from the rule.
Testing policies before you apply them
Kyverno ships a CLI that evaluates policies against resource manifests locally, without a running cluster. This is valuable in a CI pipeline where you want to know whether new policies would block existing manifests before merging either change to the main branch.
Running the Kyverno CLI
kyverno apply ./policies/ --resource ./manifests/
Apply all policies in the policies directory against all resources in manifests. Results show pass, fail and skip for each rule and resource pair.
kyverno test .
Runs structured tests defined alongside your policies. Each test specifies an input resource and the expected outcome, making it easy to verify that validate rules reject what they should reject.
The test command is especially worth building into a pull request workflow. Policies checked into Git, tested in CI with known-good manifests, then applied to the cluster through a GitOps pipeline is a setup that prevents a whole category of incidents where a new policy accidentally breaks an unrelated deployment.
Where it fits
Kyverno sits at the admission layer of a Kubernetes cluster. It is not a runtime security tool. It does not watch what pods are doing after they start. Its job is to prevent misconfigured or non-compliant resources from ever reaching etcd in the first place. The resources that do get stored have already passed your policy checks.
The natural comparison is with OPA Gatekeeper, which fills the same role. Gatekeeper uses Rego as its policy language, which is more expressive but also has a steeper learning curve. Kyverno uses YAML patterns that map directly to the Kubernetes resource model. Teams already comfortable writing Kubernetes manifests tend to find Kyverno policies more approachable. The right choice depends on how complex your policy logic needs to be and how much of your team already knows Rego.
In practice, Kyverno tends to show up alongside tools that handle the parts of security it does not: Falco for runtime threat detection, HashiCorp Vault for secrets, network policies for traffic isolation. Kyverno handles the configuration guardrails. Getting a baseline set of validate policies in place takes a day. The harder work is deciding exactly where to draw the line: strict enough to prevent real problems, permissive enough that teams can still ship without raising a ticket every time they need an exception.