Introduction to Kyverno
Kyverno (pronounced “koo-vern-oh”, Greek for “govern”) is a policy engine designed exclusively for Kubernetes. It manages admission control, mutation, validation, and even resource generation using policies written as standard Kubernetes resources. Unlike general-purpose policy engines that require a separate language, Kyverno policies are expressed as YAML — the same language you already use for Kubernetes manifests. This makes it accessible to platform engineers, DevOps teams, and developers who need to enforce security, compliance, and operational best practices without learning a domain-specific language.
Why Kyverno Matters
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Kubernetes is powerful but wide open by default. Without guardrails, developers can deploy containers with root privileges, expose services insecurely, or skip essential labels. Kyverno solves these challenges by allowing you to:
- Enforce security policies — block privileged containers, require read-only root filesystems, forbid hostPath volumes.
- Apply operational standards — automatically add labels, annotations, or resource quotas.
- Prevent misconfigurations — validate ingress patterns, require probes, restrict container registries.
- Generate complementary objects — automatically create network policies or RBAC rules when a namespace is created.
- Simplify multi-tenancy — enforce tenant isolation with minimal overhead.
Kyverno integrates natively with the Kubernetes API server as a dynamic admission controller, so it works with any kubectl, CI/CD pipeline, or GitOps tool that submits resources to the API. It also supports background scanning to detect existing policy violations in a cluster.
How Kyverno Works
Kyverno runs as a deployment in your cluster, registering webhooks for mutating and validating admission requests. When a Kubernetes resource is created, updated, or deleted, the API server sends an admission review to Kyverno. Based on policies you define, Kyverno can:
- Mutate the resource by adding defaults or overriding fields (via JSON patches).
- Validate the resource against rules and deny admission if constraints are violated.
- Generate new resources in response to events (e.g., namespace creation).
Policies are stored as custom resources (CRDs) like ClusterPolicy (cluster-scoped) and Policy (namespace-scoped). There is no separate language or server; everything lives inside Kubernetes.
Installation and Setup
Kyverno can be installed via Helm or a single YAML manifest. The Helm approach gives you more control over configuration and is recommended for production.
Install using Helm
# Add the Kyverno Helm repository
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
# Install Kyverno in the kyverno namespace
helm install kyverno kyverno/kyverno --namespace kyverno --create-namespace
Install using raw YAML
# Install the latest stable version
kubectl apply -f https://raw.githubusercontent.com/kyverno/kyverno/main/definitions/release/install.yaml
After installation, verify that the Kyverno pods are running:
kubectl get pods -n kyverno
You'll see pods for the Kyverno controller and background scanner. The controller handles real-time admission requests, while the scanner periodically checks existing resources.
Policy Structure
A Kyverno policy is a YAML document with the usual Kubernetes fields: apiVersion, kind, metadata, and spec. The spec contains rules that define what to do. A rule has:
matchorexcludeblock — select which resources the rule applies to (by kind, namespace, labels, etc.).- One or more action blocks —
mutate,validate, orgenerate. - A
contextblock (optional) — fetch data from other resources, ConfigMaps, or external sources for use in the rule.
Here is the skeleton of a ClusterPolicy:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: my-policy
spec:
rules:
- name: my-rule
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Your custom error message"
pattern:
spec:
containers:
- image: "!*:latest"
Writing Your First Policy: Disallow Latest Tag
Let's create a policy that blocks pods using container images with the :latest tag. This is a common best practice to ensure immutable deployments.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
annotations:
policies.kyverno.io/title: Disallow Latest Tag
policies.kyverno.io/category: Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/description: >-
Using the latest tag on images leads to unpredictable behavior
and should be avoided in production.
spec:
validationFailureAction: Enforce
rules:
- name: require-not-latest
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Images with tag 'latest' are not allowed"
pattern:
spec:
containers:
- image: "!*:latest"
- (image): "!*:latest"
Apply this policy:
kubectl apply -f disallow-latest.yaml
Now try creating a pod with an image like nginx:latest; the request will be denied with the message you specified.
Mutating Policies: Automatically Add Defaults
Mutation policies allow you to modify incoming resources before they are persisted. This is perfect for injecting sidecars, adding labels, or setting security contexts.
Example: Add Default Pod Security Context
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-pod-security-context
spec:
rules:
- name: add-non-root-user
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: "*"
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
This policy adds a pod-level security context and container-level restrictions to every pod that does not already specify them. The patchStrategicMerge approach merges the provided defaults with existing configurations.
Example: Add a Label from Resource Metadata
You can dynamically add labels based on the resource's own fields using variable substitution.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-namespace-label
spec:
rules:
- name: add-ns-label
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
namespace-name: "{{request.object.metadata.namespace}}"
This adds a namespace-name label to every pod, using the namespace from the request object.
Validating Policies: Advanced Patterns
Validation rules can use patterns, conditional expressions, and even JMESPath for complex logic.
Using Conditional Expressions (CEL)
Kyverno supports CEL (Common Expression Language) for fine-grained validations. This example requires that any pod with a specific label must also have a readiness probe.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-readiness-probe
spec:
rules:
- name: check-readiness-probe
match:
any:
- resources:
kinds:
- Pod
validate:
cel:
expressions:
- expression: "object.spec.containers.all(c, has(c.readinessProbe))"
message: "All containers must have readiness probes configured"
Deny by External Data (Context)
Sometimes you need to compare a request against data from a ConfigMap or another Kubernetes object. Use the context block to load that data.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: allowed-registries
spec:
rules:
- name: check-registry
match:
any:
- resources:
kinds:
- Pod
context:
- name: allowed-registries
configMap:
name: trusted-registries
namespace: kyverno
validate:
message: "Image registry not in allowed list"
pattern:
spec:
containers:
- image: "{{allowed-registries.data.registries}}"
The ConfigMap trusted-registries in the kyverno namespace contains a key registries with comma-separated registry prefixes. The pattern checks that every container image starts with one of those values.
Generating Policies
Generate rules allow Kyverno to create new Kubernetes objects automatically, reducing manual toil and ensuring consistent setups.
Example: Generate a NetworkPolicy per Namespace
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-default-network-policy
spec:
rules:
- name: gen-network-policy
match:
any:
- resources:
kinds:
- Namespace
generate:
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
name: default-deny
namespace: "{{request.object.metadata.name}}"
data:
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: []
egress: []
When a new namespace is created, Kyverno immediately generates a default-deny NetworkPolicy in that namespace, isolating pods until explicit rules are added.
Policy Testing with the Kyverno CLI
Testing policies before deploying them to a live cluster is crucial. The Kyverno CLI (kyverno) provides a test command that executes policies against local manifests and expected results.
Install the CLI
# macOS (Homebrew)
brew install kyverno
# Linux / Windows: download from GitHub releases
curl -LO https://github.com/kyverno/kyverno/releases/download/v1.10.0/kyverno-cli_v1.10.0_linux_x86_64.tar.gz
tar -xzf kyverno-cli_v1.10.0_linux_x86_64.tar.gz
sudo mv kyverno /usr/local/bin/
Write a Test Case
Test cases are defined in a separate YAML file, referencing policy files and resource files. The structure:
apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: disallow-latest-test
policies:
- ../path/to/policy.yaml
resources:
- resource.yaml
results:
- policy: disallow-latest-tag
rule: require-not-latest
resource: nginx-latest-pod
status: fail
message: Images with tag 'latest' are not allowed
- policy: disallow-latest-tag
rule: require-not-latest
resource: nginx-fixed-pod
status: pass
Run the test:
kyverno test test.yaml
The CLI outputs pass/fail counts, ensuring your policies work as intended before cluster deployment. This is a game-changer for CI/CD pipelines and GitOps workflows.
Background Scanning and Policy Reports
Kyverno continuously scans existing resources in the cluster to identify violations of validate policies. It generates PolicyReport custom resources that aggregate results. You can view violations with:
kubectl get policyreport -A
Or use the Kyverno CLI to inspect reports:
kyverno scan --policy disallow-latest-tag
This helps you audit a live cluster and track compliance drift over time.
Best Practices
- Start with validation-only policies — mutations can surprise developers; begin by blocking bad states, then gradually add safe defaults.
- Use
validationFailureAction: Auditinitially — set policies to audit mode to log violations without blocking, then switch toEnforceafter confidence is gained. - Leverage the CLI for testing — integrate
kyverno testinto your CI pipeline to validate policy changes alongside application manifests. - Organize policies with namespaces and categories — use annotations like
policies.kyverno.io/categoryandseverityto group and filter policies. - Use context carefully — fetching external data (ConfigMaps, API calls) can slow down admission; cache lookups when possible and avoid complex transformations.
- Combine policies for defense in depth — layer validation, mutation, and generation. For example, generate a NetworkPolicy, mutate pods to add security contexts, and validate image signatures.
- Version control policies as code — treat Kyverno policies like application code; store them in Git and deploy via GitOps.
- Use policy exceptions wisely — Kyverno supports
PolicyExceptionresources to exempt specific resources from a policy. Use them sparingly and document reasons. - Monitor policy performance — Kyverno provides Prometheus metrics; watch admission latency and reject rates to ensure policies don't slow down cluster operations.
Conclusion
Kyverno brings Kubernetes-native policy management to your cluster with minimal friction. Its YAML-based policy language, tight integration with the API server, and support for mutation, validation, and generation make it an essential tool for platform teams. By starting with simple validation rules, testing with the CLI, and gradually layering mutations and generators, you can enforce security, compliance, and operational consistency across every namespace and workload. Embrace policy-as-code, integrate Kyverno into your GitOps pipeline, and watch your cluster transform into a governed, predictable platform.