Deleting a Kyverno ClusterPolicy removed every NetworkPolicy it generated. Why, and how do you avoid that?
answer
- The policy owns what it created
- Uninstalling the rule uninstalls its output
- Fail-open, and nothing was denied
- A field exists to orphan the copies
- Rename equals delete plus create
basics
~20 sSynchronized downstream resources are owned by the policy that generated them, so removing the policy garbage-collects the copies. Set generate.orphanDownstreamOnPolicyDelete to true before decommissioning, or turn synchronization off first, so the objects are left behind.
solid answer
~50 sWith `synchronize: true` the generated objects are not independent copies - Kyverno tracks them as downstream resources of that rule, and deleting the policy takes them with it. That is deliberate: the policy owns what it created, so uninstalling a generator cleans up after itself. It is also how a routine policy cleanup silently removes the default-deny NetworkPolicy from every namespace at once, and because default-deny is the object being removed, the cluster fails open rather than closed. The controls are: set `generate.orphanDownstreamOnPolicyDelete: true` so downstream objects survive the policy's removal; restrict who can delete ClusterPolicies through RBAC; and back the guardrail with a separate validate rule so the enforcement does not live entirely inside the object that just vanished. The same coupling applies at the other end - delete the trigger namespace and its downstream goes with it, which is the harmless case.
go deeper
Know that a generated object is not an ordinary copy: Kyverno remembers which policy and trigger produced it, and deleting either of those can remove it.
Explain the two cascade directions - trigger deletion and policy deletion - and name the setting that lets downstream objects survive the policy being removed.
Demonstrate the operational sequence: enumerate what a policy owns, orphan or migrate before deleting, and recognise that losing a default-deny policy is a silent fail-open with no denial to alert on.
Own the standing question of whether a single deletable object should be able to remove a cluster-wide security control, and what independent detection you fund so the answer is not just care.
## Why the objects disappeared A synchronized generate rule does not fire and forget. Kyverno keeps a relationship between the policy, the trigger and the downstream object so that it can reconcile drift, and that same relationship defines lifecycle. Two deletions cascade: - **Delete the trigger** and the downstream goes with it. This is the benign direction: the namespace that owns the NetworkPolicy is being deleted anyway, and an ownerReference back to the trigger lets ordinary Kubernetes garbage collection do the work. - **Delete the policy** and the downstream objects it manages are cleaned up too. This is the direction that surprises people, and it is the one in the question. The design intent is reasonable. If a generator is uninstalled, leaving hundreds of orphaned objects that nothing owns, nothing reconciles and nobody can explain is its own kind of mess. Kyverno's default is that uninstalling the rule uninstalls its output. ## Why this particular blast radius is worse than it looks The object being removed matters. A ResourceQuota vanishing is a capacity problem you will notice. A default-deny NetworkPolicy vanishing is a **fail-open** event: in Kubernetes, a namespace with no NetworkPolicy selecting a pod allows all traffic to it. So a change that looks like tidying up policy YAML - deleting a superseded ClusterPolicy, renaming one, or letting a delivery tool prune a resource that dropped out of a directory - can flip an entire cluster from segmented to flat, with no denial, no failed pipeline and no alert. Nothing was blocked, because the mechanism here is creation, not gating. The rename case deserves calling out on its own. A generate rule's downstream is tied to the policy that owns it, so renaming the ClusterPolicy is not an edit; it is a delete plus a create. The old copies go, and the new ones arrive on the next reconcile, with a gap in between. ## The controls, in the order you should reach for them **1. Decouple the lifecycle where you actually want the objects to outlive the rule.** `generate.orphanDownstreamOnPolicyDelete: true` tells Kyverno to leave the downstream resources in place when the policy is deleted. Set it on guardrail generators that must not disappear, and set it *before* the day you need it, not during the change that removes the policy. The tradeoff is real: the surviving objects are now unmanaged, so you have accepted a cleanup obligation in exchange for not failing open. **2. Sequence the decommission deliberately.** When you genuinely are retiring a generator, do it in steps rather than with one `kubectl delete`: confirm what the policy owns, orphan or convert the downstream copies first, verify a sample namespace still has the objects, then remove the rule. Treat it like removing a controller, not like deleting a config file. **3. Restrict who can delete the policy.** The ClusterPolicy resource is now a cluster-wide security control. Write access to it - including whatever delivery mechanism applies it - should sit with the platform team and be as guarded as the RBAC that lets Kyverno's background controller create Secrets in every namespace in the first place. **4. Do not let the guardrail live only in the generated object.** Generation supplies the NetworkPolicy; it does not enforce that one exists. A separate validate rule that rejects workloads in a namespace lacking the expected policy, or a periodic report over namespaces, means the fail-open state is detected rather than assumed impossible. Two independent mechanisms fail independently. ## What to say when asked how you would find out Before deleting anything, you want to know what a policy owns. Generated objects carry labels and annotations identifying the policy and rule that produced them, so you can list the downstream fleet and count it. Do the counting in a test cluster too: the honest senior answer is that lifecycle behaviour under deletion is the part of any policy engine most worth verifying by experiment rather than by memory, because the cost of being wrong is measured in namespaces. ## The shape of the answer Name the coupling (synchronized downstream is owned, not copied), name the field that breaks it (`orphanDownstreamOnPolicyDelete`), name the direction of failure (fail-open, and silent, because nothing was denied), and name the compensating control (an independent validate rule or report, plus RBAC on the policy itself).
- You need to rename the ClusterPolicy. What happens to the generated objects?A rename is a delete plus a create, so the downstream copies owned by the old name are removed and new ones appear on the next reconcile, with a gap between. On a default-deny generator that gap is a fail-open window across every namespace, so orphan the downstream first, or make the change in a maintenance window with verification afterwards.
- Why is losing a generated default-deny NetworkPolicy worse than losing a generated ResourceQuota?A missing quota is noticed the first time something over-consumes, and it degrades toward a resource complaint. A missing NetworkPolicy is silent and fails open: a Kubernetes pod that no NetworkPolicy selects accepts all traffic. The security control is gone with no signal at all, which is why it needs a detective check independent of the generator.
- What do you check before deleting any generate policy?What it owns and how many. Generated objects carry labels and annotations naming the policy and rule that produced them, so list them and count. Then decide explicitly whether those copies should survive - orphan them if so - and verify the outcome in a test cluster first, because deletion semantics are the part most worth confirming by experiment.
saying these in an interview costs you the question
- Assumes generated objects are ordinary independent copies
- Thinks deleting a policy only stops future generation
- Treats a policy rename as a harmless in-place edit
- Cannot say why a missing NetworkPolicy fails open
- Relies solely on the generator with no independent check