← Back to list

Kyverno Generate Rules: The Immutable Fields That Break Helm Upgrades

You deploy a Kyverno generate policy.

Engineer · 2026-06-08 12:28 · 10 claps · 5.0 min read paywalled
#kubernetes #containers #devops #cloud-computing #kyverno
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

Kyverno Generate Rules: The Immutable Fields That Break Helm Upgrades

You deploy a Kyverno generate policy.

Everything works perfectly.

Namespaces are getting ConfigMaps, RBAC objects are being created automatically, and the platform team is happy.

A few weeks later, you need to make what seems like a harmless change. Maybe you want to modify the match criteria, add a new exclusion, change the generated resource name, or update a field introduced in a newer Kyverno release.

You run:

kubectl apply -f policy.yaml

and immediately get:

admission webhook "validate-policy.kyverno.svc" denied the request:
change of immutable fields for a generate rule is disallowed

At first glance, this feels strange. Kubernetes lets you update lots of resources. Why would Kyverno reject what appears to be a normal policy change?

The answer lies in how generate rules work internally and how Kyverno tracks ownership of generated resources.

Understanding this behavior is critical because it often appears during Helm upgrades, GitOps deployments, and policy migrations — usually at the worst possible moment.

Why Generate Rules Are Different

Validation and mutation policies evaluate resources and then move on.

Generate rules are different.

A generate rule creates and manages downstream resources. Once those resources exist, Kyverno must maintain a relationship between:

  • The policy
  • The generate rule
  • The triggering resource
  • The generated resource

For example:

Namespace
    │
    ▼
Generate Rule
    │
    ▼
ConfigMap

Kyverno needs a reliable way to determine:

  • Which resource was generated by which rule
  • Whether synchronization should occur
  • Whether a generated resource should be updated
  • Whether it should be deleted when ownership changes

If fundamental parts of the rule were allowed to change freely, Kyverno would have no unambiguous way to determine what should happen to resources that already exist.

Instead of making assumptions, Kyverno protects consistency by treating key portions of generate rules as immutable.

What “Immutable” Actually Means

An immutable field is a field that cannot be modified after the resource has been created.

This doesn’t mean the policy is completely locked.

It means certain structural parts of the generate rule become fixed once the policy is installed.

When those fields change, Kyverno considers it a different generate rule rather than an update to an existing one.

As a result, the update is rejected.

The Fields That Typically Trigger Problems

The exact list can vary between Kyverno releases, but immutable fields generally include the parts that define the identity and behavior of a generate rule, such as:

  • Rule name
  • Match conditions
  • Exclude conditions
  • Preconditions
  • Generated resource kind
  • Generated resource name
  • Generated resource namespace
  • Generated resource API version

These fields define what the rule targets and what it creates.

Changing them fundamentally changes the relationship between Kyverno and already-generated resources.

Why Helm Upgrades Commonly Fail

This is where most teams encounter immutable-field validation.

Imagine a Helm chart containing a ClusterPolicy:

rules:
  - name: generate-config

Months later someone updates it:

rules:
  - name: generate-default-config

From Helm’s perspective this is simply a manifest change.

From Kyverno’s perspective the rule identity changed.

When Helm performs the upgrade, Kyverno validates the update and rejects it.

The result is a failed release:

UPGRADE FAILED:
admission webhook "validate-policy.kyverno.svc" denied the request

The same issue appears when changing:

  • Match blocks
  • Exclude blocks
  • Generated resource names
  • Generated resource kinds
  • Generated resource namespaces

This is why generate policies require more planning than typical Kubernetes resources.

The Fields You Usually Can Change

Not everything is immutable.

Depending on the generate rule type and Kyverno version, updates are generally allowed for operational settings such as:

generate:
  synchronize: true

and updates to the generated resource content itself.

For example:

generate:
  data:
    key: new-value

In these situations Kyverno can safely reconcile existing resources because ownership relationships remain unchanged.

Think of it this way:

You can often change what’s inside the generated resource.

You usually cannot change the identity of the generate rule itself.

Understanding synchronize

One of the most important generate settings is:

generate:
  synchronize: true

Without synchronization, generation is typically a one-time event.

Kyverno creates the downstream resource and then stops managing it.

With synchronization enabled:

  • Deleted resources are recreated
  • Drift is corrected
  • Updates are reconciled

Example:

generate:
  synchronize: true
  apiVersion: v1
  kind: ConfigMap
  name: proxy-config

If someone modifies or deletes the ConfigMap, Kyverno restores it to the desired state.

For production environments, this is often the preferred behavior.

Understanding background

Another commonly misunderstood setting is:

spec:
  background: true

This allows Kyverno’s background controller to evaluate existing resources rather than only processing admission requests.

Without background processing:

  • New resources are evaluated
  • Existing resources may be ignored

With background processing:

  • Existing resources can be re-evaluated
  • Reconciliation becomes possible
  • Generate workflows become more consistent

Most production generate policies use background processing.

Understanding generateExisting

By default, generate rules typically affect resources created after the policy is installed.

Suppose you already have:

40 Namespaces

and then install a policy that generates a ConfigMap.

Those namespaces may not automatically receive the generated resource.

That’s where:

generate:
  generateExisting: true

becomes useful.

During policy installation, Kyverno performs a backfill operation and generates resources for existing matches.

After the initial backfill, the policy continues handling future resources normally.

This setting is particularly valuable during platform migrations and first-time policy adoption.

The Safe Way To Change Immutable Fields

Eventually you’ll need to modify something immutable.

When that happens, updating the existing policy won’t work.

The safest approach is:

Step 1: Export the current policy

kubectl get clusterpolicy <policy-name> -o yaml > backup.yaml

Step 2: Review generated resources

Identify resources currently owned by the generate rule.

kubectl get configmaps -A
kubectl get rolebindings -A

or whatever resource type your policy generates.

Step 3: Delete and recreate the policy

Update the policy manifest with the desired changes.

Then:

kubectl delete -f old-policy.yaml
kubectl apply -f new-policy.yaml

Step 4: Verify reconciliation

Check policy status:

kubectl get clusterpolicy

Check UpdateRequests:

kubectl get updaterequests -n kyverno

Investigate failures:

kubectl describe updaterequest <name> -n kyverno

UpdateRequests: The Hidden Component Most Engineers Ignore

When a generate rule executes, Kyverno doesn’t always create resources immediately.

Instead it creates an UpdateRequest.

Think of UpdateRequests as Kyverno’s work queue.

Whenever generate behavior appears broken, this should be one of the first places you investigate:

kubectl get updaterequests -n kyverno

and

kubectl describe updaterequest <name> -n kyverno

Many generate-policy troubleshooting sessions become much shorter once teams start looking here.

RBAC Still Matters

Generate rules require permissions.

If Kyverno doesn’t have permission to create the resource being generated, generation fails.

This becomes especially important when generating:

  • Custom resources
  • Cluster-scoped resources
  • Third-party operator resources

Always verify the Kyverno background controller has the necessary RBAC permissions before deploying generate policies.

A Practical Example

The following policy creates a ConfigMap in every namespace and backfills existing namespaces.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: inject-proxy-config
spec:
  background: true
  rules:
    - name: inject-proxy-configmap
      match:
        any:
          - resources:
              kinds:
                - Namespace
      generate:
        generateExisting: true
        synchronize: true
        apiVersion: v1
        kind: ConfigMap
        name: proxy-config
        namespace: "{{ request.object.metadata.name }}"
        data:
          data:
            http_proxy: "http://proxy.internal:3128"
            https_proxy: "http://proxy.internal:3128"

This policy:

  • Watches namespaces
  • Backfills existing namespaces
  • Generates ConfigMaps automatically
  • Reconciles drift
  • Keeps generated resources synchronized

It’s a straightforward example that demonstrates the lifecycle of a generate rule without introducing unnecessary complexity.

The Real Lesson

Most engineers think of policies as configuration.

Kyverno generate rules are closer to infrastructure ownership contracts.

Once deployed, Kyverno begins tracking relationships between policies and generated resources. Certain fields become part of that ownership model, which is why they’re treated as immutable.

The result can be surprising during a Helm upgrade or GitOps deployment, but the behavior exists for a good reason: it prevents Kyverno from making unsafe assumptions about resources it already owns.

Before deploying a generate policy:

  • Plan match and exclude logic carefully
  • Choose generated resource names intentionally
  • Understand synchronization behavior
  • Test upgrades in a non-production environment

Because when the error appears:

change of immutable fields for a generate rule is disallowed

the problem usually isn’t the update itself.

It’s that Kyverno is protecting the ownership relationships it has already established.


메타데이터
post_id
eab4b59f40f0
slug
kyverno-generate-rules-the-immutable-fields-that-break-helm-upgrades-eab4b59f40f0
url
https://medium.com/@m-engineer/kyverno-generate-rules-the-immutable-fields-that-break-helm-upgrades-eab4b59f40f0
canonical_url
https://medium.com/@m-engineer/kyverno-generate-rules-the-immutable-fields-that-break-helm-upgrades-eab4b59f40f0
author_url
https://medium.com/@m-engineer
status
ok
fetched_at
2026-06-09 15:37:30