The Controller with Server-Side Apply Pattern: Ending the Field Ownership Wars

2026-08-27

When multiple controllers reconcile the same resource, they trample each other. Controller A sets replicas: 3. Controller B, watching the same Deployment, does a full PUT to update the image tag — and unknowingly reverts replicas to whatever it read a second ago. The next reconcile from A puts it back. You now have a flapping resource and two controllers each convinced the other is broken.

The root cause is that full-object updates carry hidden intent. When B writes the whole Deployment, it's implicitly claiming ownership of every field — even the ones it didn't mean to touch. The API server can't tell the difference between "I want replicas to be 1" and "I read replicas as 1 and I don't care about it."

Server-Side Apply (SSA) fixes this by making ownership explicit. Each client sends only the fields it cares about, tagged with a field manager name. The API server tracks which manager owns which field in metadata.managedFields. If B sends an Apply request without replicas, it's declaring "I don't own this field" — and A's value stays untouched.

Real-world example: A Deployment managed by both Argo CD (GitOps, sets image and labels) and HPA (Horizontal Pod Autoscaler, sets replicas). Before SSA, every Argo sync would reset replicas to the Git value, HPA would scale back up, and you'd see 30-second oscillations under load. With SSA, Argo applies with manager argocd-controller omitting spec.replicas; HPA applies with manager hpa-controller owning only spec.replicas. No conflict, no flapping.

The conflict semantics matter:

Rule of thumb: if two or more controllers touch the same resource kind, use SSA and give each one a distinct field manager. The cost is roughly one extra field per managed leaf in managedFields — for a typical Deployment, ~2KB of metadata overhead. That's the price of never debugging a flapping resource at 3 AM again.

Common mistake: using force: true to make conflicts "go away." That doesn't resolve the conflict — it just makes your controller the aggressor. Now the other controller starts losing writes silently. Force is for one-time migrations, not steady-state reconciliation.

See it in action: Check out I Was Stranded on a Deadly Island… Then I Got a System That Shows Me EVERYTHING. by 1221 Manhwa Recap to see this theory applied.
Key Takeaway: Server-Side Apply replaces "last writer wins" with per-field ownership, so multiple controllers can safely share a resource by declaring exactly which fields they care about.

All newsletters