TT Lab
Get started
Learn Learning paths Courses

KCA — Kyverno Certified Associate

Why One Rule Covers Seven Controllers, and What Policies Delete

Continue in TT Lab

In one line

For a rule written for Pods, Kyverno automatically generates (autogen) rules for Pod controllers such as Deployment, StatefulSet, and CronJob and attaches them to the policy's status.autogen. Conversely, deleting resources is handled by CleanupPolicy, a kind of policy separate from validate and mutate, and by TTL labels. Since both features happen outside the rule body, you need to know under what conditions they turn on and off.

Why this was needed

A validation such as "images only from the in-house registry" is in the end a validation of Pods. But Pods are almost never created directly; Deployments, DaemonSets, StatefulSets, Jobs, and CronJobs create them. The location of the Pod template differs by controller — for a Deployment it is spec.template.spec, and for a CronJob it is spec.jobTemplate.spec.template.spec. If you copy the same rule as many times as there are controllers, when you fix one you forget the others. That is why the autogen documentation explains that it "automatically generates rules for the higher-level controllers from a rule written only for Pods."

Cleanup is a different problem. Resources pile up that were "needed when created but nobody decided when to delete," such as a Deployment left in an experimental namespace or the Pods of a Job from a few days ago. An admission controller looks only at incoming requests, so it cannot delete what already exists. That is why you need the separate controller and schedule that the cleanup documentation describes.

How it works

What autogen creates

If you create a validate rule whose match.any[].resources.kinds is only Pod, Kyverno adds two more rules under the policy's status.autogen.rules. One is autogen-<규칙이름> (the placeholder is the rule name), which targets DaemonSet, Deployment, Job, StatefulSet, ReplicaSet, and ReplicationController and moves the pattern under spec.template.spec, and the other is autogen-cronjob-<규칙이름> (the placeholder is the rule name), which targets CronJob and moves it under spec.jobTemplate.spec.template.spec. ReplicaSet and ReplicationController are also included, but the default resource filter excludes these two, so there is a caution that to actually apply it you have to adjust the resourceFilters of the Kyverno ConfigMap.

It is not only the pattern that is moved. Variables such as request.object.metadata.* in preconditions are also translated to fit the controller, to request.object.spec.template.metadata.*. If that is not the behavior you want — for example, if you want to look at the annotations of the Deployment itself — you block the translation by wrapping object in double quotes, as in request."object".metadata....

Conditions for turning it on and off

You control the behavior with the policy annotation pod-policies.kyverno.io/autogen-controllers. If you list controllers in the value, such as Deployment,Job, it generates only those, and with none it generates nothing at all. Custom resources that hold a Pod template, such as OpenKruise's CloneSet, also become targets if you write their names in this annotation.

There are also cases where it turns off automatically. If match or exclude has names, selector, or annotations, it is skipped (because those filters may not fit controllers). It also does not generate when kinds other than Pod are written together, and mutate rules that use JSON patch are excluded as well. The point to note here is that even if autogen is off, Pods created by controllers are still caught by the Pod rule. If you want to exclude Pods created by a Job, the documentation gives an example of comparing request.object.metadata.ownerReferences[].kind with AnyNotIn in preconditions.

CleanupPolicy and ClusterCleanupPolicy

There are two kinds of cleanup policy, the namespaced CleanupPolicy and the cluster-scoped ClusterCleanupPolicy, and the API is kyverno.io/v2. The structure is familiar — you select with match/exclude, narrow with optional conditions (in which the target resource's values are referenced as target.*), fetch external data with context, and write the execution time in cron format in schedule.

apiVersion: kyverno.io/v2
kind: ClusterCleanupPolicy
metadata:
  name: cleandeploy
spec:
  match:
    any:
    - resources:
        kinds: [Deployment]
        selector:
          matchLabels:
            canremove: "true"
  conditions:
    any:
    - key: "{{ target.spec.replicas }}"
      operator: LessThan
      value: 2
  schedule: "*/5 * * * *"
  deletionPropagationPolicy: Foreground

A cleanup policy always targets resources that already exist, so subjects, Roles, and ClusterRoles, which can be known only at admission time, cannot be used in match/exclude, and operations[] can be written but is ignored. The one that does the deleting is the cleanup controller, and following the principle of least privilege, you may have to give an additional ClusterRole for each resource you want to delete. The documentation gives a ClusterRole example with the delete verb and notes that if permissions are lacking, Kyverno validates and tells you when the policy is installed.

deletionPropagationPolicy decides how to handle dependent resources. Foreground deletes the dependents first and then the owner, Background deletes the owner first and the dependents asynchronously, and Orphan leaves the dependents. If you do not specify it, it follows the API server's default behavior.

TTL labels

Even without writing a policy, if you attach the cleanup.kyverno.io/ttl label to a single resource, it gets deleted. The value has two formats — an ISO 8601 absolute time (2023-10-04 or 2023-10-04T003000Z) or a remaining time from the moment the label is observed (5m, 4h, 1d). A format it does not recognize raises a warning. Kyverno watches resources that carry the label, but the actual deletion resolution is decided by the cleanup controller's ttlReconciliationInterval flag (default 1m). The propagation policy when deleting by TTL is given with the annotation cleanup.kyverno.io/propagation-policy.

Because it is a label, it connects with other features. You can use a mutate rule to automatically attach the TTL label to particular resources, or a validate rule to stop particular groups from attaching this label in particular namespaces.

Deprecation notice in the documentation

There is something to know separately from the exam scope. The kyverno.io documentation as of 2026-09 marks CleanupPolicy and ClusterCleanupPolicy as deprecated from v1.19 and removes them in v1.20, and states that the CEL-based DeletingPolicy provides the same functionality. The upgrade documentation states that ClusterPolicy and Policy (kyverno.io/v1) themselves are also deprecated at the same time. The concepts remain the same, so the content of this article is still valid, but for policies you are newly writing you need to check which kind to write them as.

What it looks like in the field

A platform team put in a "Pods must have requests/limits" policy and then tried to use exclude to leave out Pods with a particular annotation. At that moment autogen quietly turned off, and a mismatch arose in which Pods made by a Deployment were caught but the request to create the Deployment itself passed. As the documentation prescribes, when they changed it to check request."object".metadata.annotations in preconditions instead of exclude, autogen attached again.

On the cleanup side, it is common to create a ClusterCleanupPolicy and find nothing gets deleted. Usually the cleanup controller does not have delete permission on that resource, or target.* in conditions was mistakenly written as request.object.*.

What to check in the next quiz

The quiz asks about the rule names and target kinds that autogen generates, the meaning of the annotation value none, the behavior when names, selector, or annotations are present, and CleanupPolicy's schedule, target.*, the TTL label formats, and the default of ttlReconciliationInterval.