Pod Security Admission ships inside every Kubernetes cluster, but it only gives you three fixed profiles and zero room to customize. The moment a team needs to block a specific image registry, require a label, or catch a privilege-escalation flag that the built-in profiles miss, Pod Security Admission runs out of road. That gap is why so many platform teams are now pairing Kubernetes’ native Pod Security Standards with a policy engine, and Kyverno has become the default pick for teams that want YAML-native rules instead of a new language to learn.
This tutorial walks through installing Kyverno 1.19.1 on a Kubernetes 1.37.1 cluster, writing ClusterPolicy objects that enforce the Restricted Pod Security Standard, testing enforcement against both a broken pod and a clean one, and rolling the whole thing out safely with an audit period first. Kubernetes 1.37.1 shipped on September 15, 2026, and it’s the newest patch in the 1.37 line, the branch Kubernetes officially tracks as a supported minor release alongside 1.36 and 1.35, according to the Kubernetes patch release schedule. By the end, you’ll have a working policy set you can drop into any cluster, plus a troubleshooting list for the failures that actually show up in production.
None of this requires writing a new policy language or standing up a separate control plane. Kyverno policies are plain Kubernetes YAML, so a platform engineer who already knows how to write a Deployment spec can read and modify a ClusterPolicy without learning Rego or any other domain-specific syntax. That’s the main reason it has become the default admission controller for teams layering custom security rules on top of Kubernetes’ built-in Pod Security Standards instead of replacing them outright.
Why Pod Security Standards Alone Won’t Stop a Bad Pod
Kubernetes retired PodSecurityPolicy and replaced it with Pod Security Admission, a built-in controller that applies one of three labels to a namespace: privileged, baseline, or restricted. It’s simple and it ships for free, but it can only say yes or no to the rules Kubernetes itself defines. It can’t check whether an image comes from an approved registry, can’t require a specific label on every Deployment, and can’t mutate a manifest to add a missing resource limit before the object lands in etcd.
Kyverno fills that gap by running as a validating and mutating admission webhook that reads plain YAML ClusterPolicy objects instead of a separate policy language. Teams layer it on top of Pod Security Admission, using the built-in labels for the baseline guardrail and Kyverno for everything project-specific: blocking privileged containers with a clearer error message, requiring non-root execution across every workload, and demanding CPU and memory limits before a pod is ever scheduled.
Consider the kind of incident this actually prevents. A developer copies an old Deployment manifest to bootstrap a new service, carries over a leftover privileged: true flag that was only ever needed for a debugging session, and ships it. Pod Security Admission set to baseline would catch that immediately, but a namespace set to privileged, often left that way for a legacy workload, lets it straight through. A Kyverno policy scoped cluster-wide catches it regardless of what label sits on that particular namespace, and it tells the developer exactly which field to fix instead of leaving them to guess from a generic rejection.
Timing matters here too. Kubernetes 1.34 reaches end of life on October 27, 2026, and the 1.34 branch is already in maintenance mode, accepting only critical security fixes, per the official 1.34 release page. Clusters still on 1.34 are a short runway away from losing patches entirely, which makes this a reasonable moment to move to 1.37.1 and lock down admission control at the same time rather than doing the two migrations separately.
How Kyverno Sits in the Admission Request Path
Every request to create or update an object in Kubernetes passes through two admission stages before it’s persisted: mutating webhooks run first, followed by validating webhooks. Kyverno registers as both. On the mutating pass, a ClusterPolicy can rewrite part of the incoming object, adding a missing label or filling in a default resource limit. On the validating pass, a separate set of ClusterPolicy rules either lets the (possibly modified) object through or rejects it outright with the message you write into the policy.
This two-stage design is why Kyverno can do more than Pod Security Admission out of the box. PSA only runs a validating check against its three fixed profiles, so it can tell you no, but it can never fix the problem for you. Kyverno’s mutating stage means a policy can inject a sane default before the validating stage ever has to reject anything, which in practice cuts down on the number of support tickets a platform team has to field when a developer forgets one field in a Deployment spec.
The tradeoff is operational: every object creation now depends on the admission controller responding within its configured timeout. That’s a small amount of added latency per request, and it’s also why Step 2 below installs multiple replicas of the admission controller rather than one, and why the troubleshooting section later in this guide spends so much time on webhook failure modes.
Prerequisites and Versions
You don’t need a brand-new cluster to follow along, but you do need admin-level access and a cluster you’re comfortable testing admission policies on. A kind or minikube cluster works fine for a first pass before you touch production. Here’s exactly what this walkthrough assumes:
| Tool | Version used here | Why it matters |
|---|---|---|
| Kubernetes | 1.37.1 (released Sept 15, 2026) | Latest patch in the supported 1.37 branch |
| kubectl | Matching the cluster’s minor version | Avoids API skew errors on apply |
| Helm | 3.x | Installs the Kyverno chart and CRDs |
| Kyverno | 1.19.1 (released Sept 10, 2026) | Patches two CVEs fixed in this release |
| Cluster access | cluster-admin or equivalent | Needed to install CRDs and webhooks |
One compatibility note before you install anything: Kyverno’s own documentation lists its officially tested Kubernetes range as narrower than the very latest release, so running it on 1.37.1 means testing in a non-production cluster first and watching the project’s release notes closely, something Step 10 of this tutorial covers in detail.
It also helps to prepare a dedicated test namespace before you start, since several of the steps below deploy intentionally broken pods to confirm a policy actually fires. Call it policy-test or similar, and keep it separate from anything a teammate might be using, so a denied pod in Step 8 doesn’t get confused with a real deployment failure somewhere else in the cluster.
Step 1: Confirm Your Kubernetes Cluster Version
Start by checking exactly what you’re running. Admission webhooks are sensitive to API version mismatches, so skipping this step causes confusing errors later.
kubectl version
kubectl get nodes -o wide
Expected output looks like this:
Client Version: v1.37.1
Server Version: v1.37.1
NAME STATUS ROLES AGE VERSION
node-pool-1-a Ready control-plane 40d v1.37.1
node-pool-1-b Ready <none> 40d v1.37.1
If you see 1.34.x in that output, remember the October 27, 2026 end-of-life date mentioned above and plan an upgrade path before you lean on this cluster for anything long-term. If you’re on 1.36.5 or 1.35.9, both are still inside the three supported minor releases Kubernetes maintains, so this tutorial works the same way, just swap the version numbers in your head as you read.
Step 2: Install Kyverno 1.19.1 With Helm
Add the official Helm repository, pull the latest chart index, and install Kyverno into its own namespace. Keeping Kyverno isolated in its own namespace makes it much easier to exclude it from its own policies later, something you’ll want in Step 9.
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
# Confirm which chart version maps to app version 1.19.1
helm search repo kyverno/kyverno --versions | head -20
helm install kyverno kyverno/kyverno \
--namespace kyverno \
--create-namespace \
--set admissionController.replicas=3
Running the install with three admission controller replicas matters more than it looks. A single replica means a pod restart or node drain takes your entire admission layer offline for a few seconds, and depending on your webhook failure policy, that either blocks every deployment in the cluster or silently lets everything through. Neither is what you want.
Step 3: Verify Kyverno Is Running
Before writing any policies, confirm every Kyverno component reports Ready. The install creates several deployments: an admission controller, a background controller, a reports controller, and a cleanup controller.
kubectl get pods -n kyverno
kubectl get crds | grep kyverno
NAME READY STATUS RESTARTS
kyverno-admission-controller-7f8c9d9f6b-2xk4p 1/1 Running 0
kyverno-admission-controller-7f8c9d9f6b-91mzq 1/1 Running 0
kyverno-admission-controller-7f8c9d9f6b-f3jtl 1/1 Running 0
kyverno-background-controller-5d8b7c6f9-qp2wn 1/1 Running 0
kyverno-cleanup-controller-6c9f5d7b8-xr4lt 1/1 Running 0
kyverno-reports-controller-8b7c6d5f9-k2tmn 1/1 Running 0
If any pod sits in CrashLoopBackOff, check the logs before moving on. A common first-run cause is a resource-constrained node refusing to schedule the admission controller, which you’ll see as Pending rather than CrashLoopBackOff, and the fix is usually freeing up CPU or lowering the chart’s default resource requests for a test cluster.
Step 4: Pod Security Standards Levels, Explained
Kubernetes defines three Pod Security Standards levels, and Kyverno policies typically map to one of the stricter two. Knowing exactly what each level blocks helps you decide where to start.
Baseline: the minimum bar
Baseline blocks the most obviously dangerous settings: privileged containers, host namespace sharing, and most dangerous Linux capabilities. It still allows things like running as root, which is why most security-conscious teams treat it as a floor, not a target.
Restricted: the hardened target
Restricted adds everything Baseline misses: it requires running as non-root, drops all Linux capabilities by default, forces a seccomp profile, and blocks privilege escalation outright. It’s the level most compliance frameworks expect, and it’s the target this tutorial builds toward.
| Check | Baseline | Restricted |
|---|---|---|
| Privileged containers | Blocked | Blocked |
| Host namespaces (PID, IPC, network) | Blocked | Blocked |
| Run as root | Allowed | Blocked |
| Privilege escalation | Mostly allowed | Blocked |
| Linux capabilities | Dangerous ones dropped | All dropped by default |
| Seccomp profile | Not required | Required |
The Kyverno policies in the next three steps enforce the Restricted-level checks that teams hit most often in practice: privileged containers, root execution, and missing resource limits. Resource limits aren’t technically part of Pod Security Standards, but bundling them into the same rollout catches the single most common cause of noisy-neighbor incidents on shared nodes.
Step 5: Write a ClusterPolicy That Blocks Privileged Containers
Start in Audit mode, not Enforce. Audit mode records violations in a PolicyReport without blocking anything, which gives you a safe window to see how many existing workloads would break before you flip the switch.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privileged-containers
spec:
validationFailureAction: Audit
background: true
rules:
- name: privileged-containers
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Privileged containers are not allowed."
pattern:
spec:
=(ephemeralContainers):
- =(securityContext):
=(privileged): "false"
=(initContainers):
- =(securityContext):
=(privileged): "false"
containers:
- =(securityContext):
=(privileged): "false"
Apply it with kubectl apply -f disallow-privileged-containers.yaml and give it a day or two before moving to the next rule. Rolling out three new policies at once makes it much harder to tell which one caused a denial when something breaks.
Step 6: Enforce Non-Root Execution
This is the rule that catches the most legacy workloads, since plenty of older container images default to running as root. Keep this one in Audit mode longer than the others.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-run-as-nonroot
spec:
validationFailureAction: Audit
background: true
rules:
- name: run-as-non-root
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Containers must set runAsNonRoot: true."
pattern:
spec:
containers:
- securityContext:
runAsNonRoot: true
Pair this policy with a mutation rule if you manage a large fleet of older images. A mutating ClusterPolicy can inject a default runAsUser value when one is missing, which fixes simple cases automatically instead of forcing every team to edit their Deployment YAML by hand. That one change alone often clears the majority of violations a first audit scan turns up, since most teams aren’t deliberately running as root, they simply never set the field because the base image’s default happened to work.
Step 7: Require CPU and Memory Requests and Limits
Pods without resource requests get the lowest scheduling priority and, worse, pods without limits can starve every other workload on a node during a traffic spike. This rule closes both gaps in one policy.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-requests-limits
spec:
validationFailureAction: Audit
background: true
rules:
- name: validate-resources
match:
any:
- resources:
kinds:
- Pod
validate:
message: "CPU and memory requests and limits are required."
pattern:
spec:
containers:
- resources:
requests:
memory: "?*"
cpu: "?*"
limits:
memory: "?*"
cpu: "?*"
The "?*" pattern simply means “any non-empty value is fine,” so this rule doesn’t dictate specific numbers, it just makes sure every container declares something.
Step 8: Deploy an Intentionally Insecure Pod
Before trusting any of these policies, prove they actually catch something. Switch one policy to Enforce temporarily in a throwaway test namespace and try to deploy a pod that breaks every rule at once.
# bad-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: bad-pod
namespace: policy-test
spec:
containers:
- name: app
image: nginx:1.27
securityContext:
privileged: true
runAsNonRoot: false
$ kubectl apply -f bad-pod.yaml
Error from server: error when creating "bad-pod.yaml":
admission webhook "validate.kyverno.svc-fail" denied the request:
resource Pod/policy-test/bad-pod was blocked due to the following policies
disallow-privileged-containers:
privileged-containers: 'validation error: Privileged containers are not allowed.
rule privileged-containers failed'
require-run-as-nonroot:
run-as-non-root: 'validation error: Containers must set runAsNonRoot: true.
rule run-as-non-root failed'
That denial message is the whole point of this exercise. It names the exact policy and rule that failed, which is far more actionable than a generic “forbidden” error from a raw PodSecurity admission rejection.
Step 9: Deploy a Compliant Pod and Confirm Admission
Now fix every field the previous pod broke and confirm it goes through cleanly.
# good-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: good-pod
namespace: policy-test
spec:
containers:
- name: app
image: nginx:1.27
securityContext:
privileged: false
runAsNonRoot: true
runAsUser: 101
allowPrivilegeEscalation: false
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "250m"
memory: "256Mi"
$ kubectl apply -f good-pod.yaml
pod/good-pod created
$ kubectl get pod good-pod -n policy-test
NAME READY STATUS RESTARTS AGE
good-pod 1/1 Running 0 8s
If this pod still gets denied, re-read the error message carefully. Kyverno names the specific rule that failed, so a lingering denial almost always points to one field you missed rather than a problem with the policy engine itself.
Step 10: Move From Audit to Enforce the Right Way
Once your test pods behave as expected, it’s time to roll these policies out cluster-wide, but not all at once. Pull a report of every existing violation first.
kubectl get policyreport -A -o json | \
jq '[.items[].results[] | select(.result=="fail")] | length'
That number tells you exactly how many workloads will break the moment you flip validationFailureAction from Audit to Enforce. If it’s non-zero, fix those workloads first, or scope the policy to specific namespaces using a match.any.resources.namespaces filter so you can enforce it gradually, team by team, instead of cluster-wide in one move.
Keep a rollback plan ready too. Patching a ClusterPolicy back to Audit takes one command and seconds to apply, which is a far faster recovery path than asking an on-call engineer to find and delete the offending resource under pressure. Treat the switch to Enforce as a change you can reverse in under a minute, and make sure whoever is on call that week knows the exact patch command before you flip it.
When you’re ready, change the field and reapply:
kubectl patch clusterpolicy disallow-privileged-containers \
--type merge -p '{"spec":{"validationFailureAction":"Enforce"}}'
Step 11: Inspect Kyverno Policy Reports
Kyverno’s background controller continuously scans existing resources, not just new ones, and writes the results into PolicyReport objects you can query anytime.
kubectl get polr -A
kubectl describe polr -n policy-test polr-ns-policy-test
NAMESPACE NAME PASS FAIL WARN ERROR
policy-test polr-ns-policy-test 3 0 0 0
production polr-ns-production 41 2 0 0
That second row is the useful one in a real cluster. Two failing results in production means two workloads still need fixing before you enforce the rule there, and the report tells you exactly which pods and which rule, instead of leaving you to guess.
Step 12: Pin Versions and Handle the Compatibility Gap
This step matters more than it sounds like it should. Kyverno 1.19 was released in August 2026, and its community support window runs for roughly three months, with patches limited to critical bugs and high-severity CVEs, according to the Kyverno release support page. Kyverno 1.19.1 followed on September 10, 2026, and it exists specifically to patch two issues: CVE-2026-39821, fixed by bumping Go and the x/net dependency, and CVE-2026-56853, fixed with a separate Go update, both confirmed in the Kyverno GitHub releases page.
Pin your Kyverno version explicitly in Helm rather than letting helm upgrade pull the newest chart automatically, and track the project’s support window so you’re not caught running an unsupported release when the next CVE lands. On the Kubernetes side, 1.38.0-alpha.2 landed October 7, 2026, but alpha releases are not production targets, so stay on 1.37.1 or the 1.36 and 1.35 branches until a stable 1.38 release ships.
If you manage clusters across multiple providers, double-check their own Kubernetes support windows too. IBM Cloud’s release notes confirm that Kubernetes 1.33 support ended as of October 14, 2026, pushing customers to upgrade to at least 1.34, per IBM Cloud’s container release notes, while Oracle’s OCI Kubernetes Engine added 1.37.0 support in a preview release dated October 8, 2026, according to Oracle Cloud Infrastructure’s release notes. Managed Kubernetes version support doesn’t move in lockstep across clouds, so verify your specific provider before assuming a version is safe everywhere.
A practical way to stay ahead of both calendars is to put the Kubernetes and Kyverno version numbers in the same place a human actually looks, such as a README in your policy repository or a dashboard your platform team already checks weekly. Write down the Kubernetes minor version each cluster runs, the Kyverno chart and app version installed, and the dates both are due to lose support. Revisit that sheet every time a new Kubernetes minor or Kyverno release ships, rather than discovering a support gap only after a CVE forces the question.
Common Pitfalls When Rolling Out Kyverno Policies
- Enforcing on day one. Skipping the Audit period is the single most common cause of an outage during a Kyverno rollout. A team that writes a policy, sets it straight to Enforce, and applies it cluster-wide can end up blocking every existing Deployment that happened to violate the new rule, including ones that have run safely in production for months. Give every new policy at least a few days in Audit mode, read the PolicyReport counts, and only flip to Enforce once that count hits zero.
- Not excluding the Kyverno namespace itself. A policy that applies cluster-wide without excluding
kyvernocan end up blocking Kyverno’s own pods during an upgrade or a restart, which locks you out of fixing anything through the normal admission path and forces a messier recovery, like temporarily deleting the validating webhook configuration. - Writing one giant policy instead of several focused ones. A single ClusterPolicy with a dozen rules makes denial messages harder to read, since the error has to describe which of twelve conditions failed, and it makes it painful to disable just one rule for a specific team without touching the other eleven.
- Ignoring the Kubernetes and Kyverno compatibility gap. Running the newest Kubernetes release against a Kyverno version tested on older minors can introduce subtle admission behavior differences that only show up under load, long after the initial rollout looked clean in testing.
- Letting TLS certificates expire. Kyverno’s admission webhook relies on valid certificates to communicate with the API server, and an expired cert fails every admission request silently until someone notices deployments are stuck and starts digging through logs.
- Setting an aggressive
failurePolicy: Failtoo early. Until you trust your policies completely, a webhook outage combined with a Fail policy blocks every deployment cluster-wide, including the emergency fix you need to apply to resolve the outage in the first place. - Treating Kyverno as a replacement for image scanning. Admission policies catch misconfiguration at deploy time, but they don’t look inside a container image for known vulnerabilities. Pair Kyverno with a scanner in your CI pipeline rather than assuming policy enforcement alone covers supply-chain risk.
Troubleshooting Guide
- Pods stuck in Pending with an admission webhook error: check
kubectl get pods -n kyvernofirst. If the admission controller itself isn’t Ready, the webhook can’t respond to the API server, and every pod creation hangs or fails outright depending on your failure policy setting. - Every deployment gets denied immediately after applying a new policy: you likely set
validationFailureAction: Enforcebefore testing. Switch it back to Audit, check the policy report for the real failure count, then re-enforce once violations hit zero. kubectl applyhangs for 10+ seconds and then times out: the webhook’stimeoutSecondsvalue is too low for how long your policy rules take to evaluate, especially once you’ve stacked several rules against the same resource. Raise the timeout in the webhook configuration or simplify the rule’s match block.- A policy report shows “pass” for a pod that should obviously fail: the
match.anyblock is probably too narrow, often because of a typo inapiVersionorkind, or because the rule only matches a specific namespace you forgot about. Re-check the match section against the exact resource type and scope. - Pods in a brand-new namespace bypass every policy: an exclude rule using a namespace label is probably catching the new namespace by default, since many starter policies exclude anything labeled as system or infrastructure. Either label the namespace explicitly or tighten the exclude condition to a specific list of namespace names.
- A Kyverno version upgrade breaks existing policies: a major Helm chart bump can change the underlying CRD schema in ways that aren’t backward compatible. Back up your ClusterPolicy objects with
kubectl get cpol -o yaml > backup.yamlbefore any upgrade, and read the chart’s changelog for breaking changes first. - Kyverno logs show “x509: certificate signed by unknown authority”: the self-managed webhook certificate has expired or rotated unexpectedly, breaking the trust chain between the API server and the admission controller. Restarting the admission controller pods usually forces regeneration, or you can trigger it manually through Kyverno’s certificate management.
- The admission controller gets CPU-throttled under load: the chart’s default resource requests may be too low for your cluster’s traffic, especially during a mass deployment or a cluster-wide rollout. Raise requests and limits on the Kyverno deployment itself, and scale replicas rather than relying on a single pod to absorb every request.
- PolicyReports never populate for resources that already existed before the policy was applied: check that
background: trueis set on the policy. With background scanning disabled, Kyverno only evaluates new admission requests going forward, not objects already running in the cluster before the policy was created. - Policy denials reference a rule name that no longer exists in your YAML: you likely edited the ClusterPolicy without reapplying it, or an older version is still cached from a previous
kubectl apply. Runkubectl get cpol <name> -o yamlto confirm what the cluster is actually running before debugging further.
Advanced Tips and Kyverno vs Gatekeeper
Once the basic policies are stable, a few advanced features are worth adding. Use a PolicyException resource to carve out narrow, auditable exceptions for specific workloads instead of excluding an entire namespace, which keeps your exception list visible and reviewable in version control rather than buried inside a broad match condition nobody remembers writing. Turn on image verification with verifyImages to require cosign-signed images on top of the Pod Security Standards checks, closing the supply-chain gap that PSS was never designed to cover. And enable Kyverno’s autogen controls so that rules written for Pods automatically apply to the Deployments, StatefulSets, and Jobs that create them, saving you from writing the same rule three times for three different controller kinds.
Testing policies before they ever reach a cluster is worth building into your pipeline too. The Kyverno CLI can evaluate a ClusterPolicy against a set of sample manifests from a CI job, catching an obviously broken rule before a merge rather than after it’s already denying production deployments. Treat your policy YAML the same way you’d treat application code: store it in a repository, review changes through a pull request, and run the CLI check as a required status before merge.
At scale, also watch how many PolicyReport objects your cluster accumulates. A cluster running hundreds of namespaces with several policies each can generate a large number of report objects, and Kyverno’s cleanup controller is what prunes stale ones automatically. If you notice etcd growing unexpectedly after rolling out several policies, check that the cleanup controller is healthy before assuming the policies themselves are the problem.
Kyverno isn’t the only admission controller option. Gatekeeper, built on Open Policy Agent, is the other common choice, and the two take different approaches worth understanding before committing.
| Factor | Kyverno | Gatekeeper (OPA) |
|---|---|---|
| Policy language | Native Kubernetes YAML | Rego, a separate policy language |
| Mutation support | Built in | Limited, mutation is a separate CRD |
| Learning curve | Lower for Kubernetes-only teams | Higher, Rego takes time to learn |
| Image verification | Built in via verifyImages | Requires external tooling |
| Best fit | Teams that want Kubernetes-native rules | Teams already standardized on OPA elsewhere |
If your organization already uses OPA for other policy decisions outside Kubernetes, that consistency can outweigh Kyverno’s gentler learning curve. For teams starting from zero, Kyverno’s YAML-native approach usually gets a working policy in front of a security reviewer faster, since the reviewer doesn’t need to learn Rego just to understand what a rule does. Neither tool is strictly better in every situation. Gatekeeper tends to fit organizations that already run OPA against cloud infrastructure or CI pipelines and want one policy engine everywhere, while Kyverno tends to fit teams whose policy needs are Kubernetes-specific and who’d rather not introduce a second language into the stack just to gate pod creation.
The Complete Working Policy Set
Save the block below as kyverno-pss-policies.yaml and apply it with kubectl apply -f kyverno-pss-policies.yaml to install all three policies from this tutorial in one pass, still in Audit mode so you can measure impact before enforcing anything.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privileged-containers
spec:
validationFailureAction: Audit
background: true
rules:
- name: privileged-containers
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Privileged containers are not allowed."
pattern:
spec:
containers:
- =(securityContext):
=(privileged): "false"
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-run-as-nonroot
spec:
validationFailureAction: Audit
background: true
rules:
- name: run-as-non-root
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Containers must set runAsNonRoot: true."
pattern:
spec:
containers:
- securityContext:
runAsNonRoot: true
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-requests-limits
spec:
validationFailureAction: Audit
background: true
rules:
- name: validate-resources
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "CPU and memory requests and limits are required."
pattern:
spec:
containers:
- resources:
requests:
memory: "?*"
cpu: "?*"
limits:
memory: "?*"
cpu: "?*"
From here, the workflow is the same one covered in Step 10: pull the PolicyReport counts, fix whatever shows up as a failure, and flip each policy to Enforce once its failure count hits zero, one policy and one namespace at a time.
Frequently Asked Questions
Does Kyverno replace Pod Security Admission, or work alongside it?
Alongside it. Most teams keep Pod Security Admission’s baseline label on every namespace as a floor and use Kyverno for everything more specific, like image verification or custom labeling rules.
Will installing Kyverno slow down pod scheduling?
Admission webhooks add a small amount of latency to every create and update request, but a properly resourced Kyverno deployment with multiple replicas keeps that overhead low. The bigger risk is an undersized deployment getting CPU-throttled under load, covered in the troubleshooting section above.
What happens if the Kyverno admission controller goes down entirely?
It depends on each webhook’s failurePolicy setting. A Fail policy blocks all matching requests until Kyverno recovers, which is safer but riskier during an outage. An Ignore policy lets requests through unchecked, which keeps the cluster usable but defeats the purpose of the policy during that window.
Can I test a policy without risking production workloads?
Yes. Keep validationFailureAction set to Audit and scope the policy’s match block to a dedicated test namespace first, exactly as shown in Steps 8 and 9, before expanding it cluster-wide.
Is Kyverno 1.19.1 safe to run on Kubernetes 1.37.1 in production?
Kyverno’s published support matrix at the time of the 1.19 release listed older Kubernetes minors as the officially tested range, so running it on 1.37.1 means validating it in staging first and watching for Kyverno’s own compatibility updates rather than assuming full support out of the box.
How long does Kyverno support a given release?
Roughly three months of community patch support, limited to critical bugs and high or critical severity CVEs, which is why pinning your version and tracking the release calendar matters more than it would for a tool with a longer support window.
Do I need Helm to install Kyverno?
Helm is the most common path and the one used throughout this tutorial, but Kyverno also publishes static YAML manifests for teams that manage clusters without Helm. The policy objects themselves work identically either way.
What’s the fastest way to see which existing workloads would fail a new policy?
Apply the policy in Audit mode with background: true, wait for the background scan to finish, then query PolicyReport objects across every namespace as shown in Step 11. That gives you a full count of failures before you risk enforcing anything.
Should I enforce these policies cluster-wide or team by team?
Team by team almost always works better for the first rollout. Scope the ClusterPolicy’s match block to specific namespaces using a namespaces list, enforce it for one team once their PolicyReport count hits zero, and repeat for the next team. A single cluster-wide flip is faster to write but far riskier, since one overlooked legacy workload anywhere in the cluster can turn into an incident the moment Enforce takes effect.
