Skip to main content
A stuck controller is easiest to diagnose if you treat it like a detective case. The cluster leaves footprints; follow them from a noisy symptom to the root cause. This guide walks through a real example: reproduce the failure, collect evidence, locate the rejection in the API server, understand the root cause, and apply a targeted fix.

Reconcile implementation (shortened)

Start with a representative Reconcile method (only the relevant parts are shown):
A single bad label in the controller can produce a noisy reconcile loop. The manager and controller processes may be running, but repeatedly failing reconciliation—like an engine running while a car is stuck in the mud.

Example of a buggy helper that builds a Deployment

This helper injects the WebApp’s metadata.resourceVersion into labels used for both the Deployment selector and the Pod template. Because resourceVersion changes on every write, the controller will generate a different selector on each pass:
Injecting a changing field into a selector or any API field that is immutable triggers repeated update attempts and API rejections.

Reproduce and capture logs

Start the manager with debug logging and capture the output. The live terminal shows what is happening now; the saved file is your evidence bag to grep through later. Example manager startup logs (captured):
Apply the sample CR so the controller has a single object to reconcile:
Watch metadata.generation and metadata.resourceVersion side by side:
  • generation reflects user intent (changes to spec).
  • resourceVersion reflects cluster record changes.
If generation remains at 1 while resourceVersion keeps increasing, the controller is repeatedly writing the object—often a sign the controller is causing the writes. Measure the noisy reconcile behavior with a quick grep of the operator log:
Now search the debug log for Deployment updates or API errors (case-insensitive):
You want to find API rejection messages such as “field is immutable” which indicate the controller attempted an invalid update.
The image shows a Visual Studio Code window with a terminal output displaying an error related to a Kubernetes deployment. The error message indicates an issue with an immutable field in a deployment configuration.

The failure in the logs

The top-level INFO logs look fine, but the debug/ERROR lines contain the root failure:
Root cause: the controller uses the changing metadata.resourceVersion in the Deployment selector. Since Kubernetes treats spec.selector as immutable after creation, each reconcile tries to update the selector to a new value and the API rejects it.

Fix: make selectors stable

Stop feeding a changing value into a stable identity field. Remove resourceVersion (or any other constantly changing value) from the Deployment spec.selector and ensure the pod template labels are stable and match the selector. Corrected helper (no resourceVersion in selector or pod labels):
  1. Stop the manager.
  2. Edit the controller code to remove changing values from selectors/template labels.
  3. Rebuild and restart the manager.
  4. Observe operator logs and the WebApp resource; the reconcile count should drop to normal levels.
If you prefer to recreate the Deployment after code changes, you can delete the problematic Deployment first and let the controller create a correct one after restart:
When the symptom (rapid reconciling with failing updates) stops and the controller performs normal reconciles, the case is closed.
Important: Never put a changing cluster-controlled value (such as metadata.resourceVersion) into a Deployment selector or any field that the API treats as immutable. Doing so will repeatedly trigger rejected updates and a tight reconcile loop.

Quick triage checklist

Root cause summary

Root cause summary: never place a changing cluster-controlled value (like metadata.resourceVersion) into a Deployment selector (or any field that the API treats as immutable). Keep selectors stable and match them to pod template labels that do not change across reconciles.
The debugging pattern matters more than any single fix:
  • Start with the symptom.
  • Separate user intent (generation) from controller writes (resourceVersion).
  • Measure the behavior.
  • Search logs for API refusals.
  • Be suspicious when a stable identity field is fed changing data.

Watch Video

Practice Lab