Kubernetes lets you deploy and manage containers at scale, but out of the box it won't stop you
from doing risky things — like running containers with the latest tag or
deploying into the default namespace. There are no built-in guardrails.
Kyverno fixes that. It's a policy engine built for Kubernetes that lets you
write rules as plain YAML — no new language to learn — that automatically check,
modify, or even create resources whenever someone runs kubectl apply.
In this guide, we'll take a real policy — one that blocks the latest image
tag — and walk through every step: exporting it from a live cluster, cleaning it up,
applying it to a fresh environment, and testing that it actually works.
A Kyverno policy is just a Kubernetes resource (a YAML file you kubectl apply)
that describes a rule you want enforced. Policies can do three things:
latest tag")There are two scopes:
To see policies in your cluster:
kubectl get cpol -n kyverno --context my-cluster-aks
Example output:
NAME READY AGE
check-ingress-annotation True 50d
deploy-cdc-sync-to-app-namespaces True 49d
disallow-default-namespace True 50d
disallow-latest-tag True 50d
Some example policies:
kubectl get cpol -n kyverno --context my-cluster-aks -o yaml > kyverno-cluster-policies.yaml
kubectl get cpol disallow-latest-tag -n kyverno --context my-cluster-aks -o yaml > disallow-latest-tag-policy.yaml
Exported YAML contains runtime metadata that must be removed before reapplying.
Remove fields like:
metadata:
annotations:
creationTimestamp:
resourceVersion:
uid:
managedFields:
generation:
status:
Note: The status section must always be removed. It contains runtime data and will cause apply errors.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: Enforce
rules:
- name: require-image-tag
match:
any:
- resources:
kinds:
- Pod
validate:
message: An image tag is required.
pattern:
spec:
containers:
- image: "*:*"
- name: validate-image-tag
match:
any:
- resources:
kinds:
- Pod
validate:
message: Using 'latest' is not allowed.
pattern:
spec:
containers:
- image: "!*:latest"
❌ Not allowed:
image: nginx:latest
image: nginx
✅ Allowed:
image: nginx:1.25.3
Apply this policy to your cluster:
kubectl apply -f disallow-latest-tag-policy.yaml --context k3d-user-management-local
If Kyverno is not installed in this cluster, the command above will fail with:
error: resource mapping not found for name: "disallow-latest-tag" namespace: ""
from "disallow-latest-tag-policy.yaml": no matches for kind "ClusterPolicy" in version "kyverno.io/v1"
ensure CRDs are installed first
This happens because Kyverno is not installed in this cluster. Kubernetes only
understands built-in resources like Pod, Deployment, and Service. But ClusterPolicy
is a Custom Resource (CRD) provided by Kyverno. No Kyverno = No CRD = error.
First, add the Kyverno Helm repo:
helm repo add kyverno https://kyverno.github.io/kyverno/
Then switch your kubectl context. Helm does not support --context
in helm install, so you need to switch first:
kubectl config use-context k3d-user-management-local
Now install Kyverno:
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
kubectl get pods -n kyverno
kubectl get crds | grep kyverno
You should see:
clusterpolicies.kyverno.io
policies.kyverno.io
kubectl apply -f disallow-latest-tag-policy.yaml
kubectl get cpol
If this returns your policy, Kyverno is installed correctly and the policy is active.
Create a file called bad-pod.yaml with the following contents:
apiVersion: v1
kind: Pod
metadata:
name: bad-pod
spec:
containers:
- name: nginx
image: nginx:latest
Apply it:
kubectl apply -f bad-pod.yaml
Kyverno blocks it. You'll see an error like this:
Error from server: error when creating "bad-pod.yaml": admission webhook
"validate.kyverno.svc-fail" denied the request:
resource Pod/default/bad-pod was blocked due to the following policies
disallow-latest-tag:
validate-image-tag: 'validation error: Using a mutable image tag e.g. latest
is not allowed. rule validate-image-tag failed at path
/spec/containers/0/image/'
Create a file called good-pod.yaml with the following contents:
apiVersion: v1
kind: Pod
metadata:
name: good-pod
spec:
containers:
- name: nginx
image: nginx:1.25
Apply it:
kubectl apply -f good-pod.yaml
This time it works:
pod/good-pod created
The flow looks like this:
kubectl apply → API Server → Kyverno webhook → ❌ BLOCK
Kyverno logs the reason inside the admission controller pod. When Kyverno blocks a request,
the Pod is never created, so kubectl describe pod bad-pod won't
work. But you can still see what happened in two ways:
kubectl logs -n kyverno deploy/kyverno-admission-controller -f
kubectl get events --sort-by=.metadata.creationTimestamp
You'll see something like:
LAST SEEN TYPE REASON OBJECT MESSAGE
13m Warning PolicyViolation clusterpolicy/disallow-latest-tag Pod default/bad-pod:
[validate-image-tag] fail (blocked); validation error: Using a mutable image tag
e.g. 'latest' is not allowed. rule validate-image-tag failed at path
/spec/containers/0/image/
Edit your disallow-latest-tag-policy.yaml and change the validation action:
spec:
validationFailureAction: Audit
Then re-apply:
kubectl apply -f disallow-latest-tag-policy.yaml
clusterpolicy.kyverno.io/disallow-latest-tag configured
Now the bad pod goes through:
kubectl apply -f bad-pod.yaml
pod/bad-pod created
The policy still runs, but it only logs violations instead of blocking them.
Scale down the Kyverno admission controller to stop all policy enforcement:
kubectl scale deployment kyverno-admission-controller -n kyverno --replicas=0
The cluster becomes unprotected — no policies perform any validations or blockings.
To re-enable:
kubectl scale deployment kyverno-admission-controller -n kyverno --replicas=1
Add this annotation to your pod YAML to bypass Kyverno for a specific resource:
metadata:
annotations:
policies.kyverno.io/ignore: "true"
kubectl delete cpol disallow-latest-tag
The policy is deleted and no longer enforced.
Kyverno brings policy-as-code into Kubernetes in a very natural way. Instead of relying on external tools, you define security rules as Kubernetes resources.
In production environments, policies like disallow-latest-tag are critical to ensure immutability, traceability, and safer deployments.