← Back to DevBytes

Kyverno Policy Engine: Complete Implementation Guide

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:

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:

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:

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

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.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles