kubernetes

Server-Side Apply | Kubernetes
Kubectl Create vs Kubectl Apply

Core Idea

Client-side apply lets kubectl merge diffs locally and can fight other writers; server-side apply hands conflicts to the API server through field ownership, so it is explicit who owns which field.

  • CSA: kubectl computes the diff and patches from the client, which breaks down when multiple managers edit the same object.
  • SSA: each writer is a field manager, the server detects conflicts, and collaborative edits become possible.
  • The note keeps a quick comparison table plus a deep dive on server-side apply behavior.

Transclude of 3.-interacting-with-kubernetes#managing-objects

Client-Side Apply (CSA)

This is the original, default behavior of kubectl apply.

How it works:

  • All the “diffing” logic happens on your local machine (in kubectl).
  • kubectl stores the last applied configuration as a JSON blob in an annotation on the object: kubectl.kubernetes.io/last-applied-configuration.
  • On each apply, it computes a three-way merge using:
    1. The last-applied-configuration annotation (what you applied last time)
    2. The current live object on the cluster
    3. The new configuration you’re applying now
  • Based on this diff, it figures out which fields to add, update, or remove.

Limitations

  • The annotation has a size limit (~256KB), which can be an issue for large objects.
  • Field ownership tracking is fragile — it doesn’t know which controller or user owns which field, only what was in your last local apply.
  • Merge conflicts between multiple tools/users managing the same object aren’t well handled — you can accidentally overwrite fields another tool manages.
  • Merge logic is client-side, so behavior can vary depending on the kubectl version.

Server-Side Apply (SSA)

Introduced as beta in Kubernetes 1.16 and GA (stable) since 1.22, SSA moves the merging logic into the API server itself.

How it works:

  • Instead of a last-applied-configuration annotation, the API server tracks field ownership using managedFields metadata on the object.
  • Each field is associated with a “field manager” (e.g., kubectl, a controller, or a CI/CD tool) that last set it.
  • When you apply, the API server merges your intent with the live object, based on who owns which field — it can detect and report conflicts if two managers try to own the same field.
  • You use it via kubectl apply --server-side (or set it as default in newer kubectl behavior for certain workflows).

Advantages

  • No annotation size limits — no giant JSON blob stored on the object.
  • Proper multi-actor field ownership — multiple controllers/users can co-manage different parts of the same object safely.
  • Conflict detection: if another manager owns a field you’re trying to change, the API server will reject the apply unless you pass --force-conflicts.
  • More consistent behavior since logic lives in the API server, not scattered across kubectl client versions.

Quick Comparison

AspectClient-Side ApplyServer-Side Apply
Where merge happenskubectl (client)API server
Ownership trackinglast-applied-configuration annotationmanagedFields metadata
Size limitsYes (~256KB annotation)No
Multi-owner conflict detectionPoor/noneBuilt-in
Commandkubectl apply -f file.yamlkubectl apply --server-side -f file.yaml
StatusLegacy defaultRecommended going forward

Tip

If you’re mixing tools (e.g., kubectl, Helm, a custom operator, GitOps tools like ArgoCD/Flux) managing the same resources, Server-Side Apply is strongly recommended because it handles shared ownership and conflicts far more gracefully. Many GitOps tools have already switched to SSA by default for this reason.


Server Side Apply Deep Dive

Introduction

A Kubernetes object is shared state.

A Deployment is not managed solely by the YAML file that created it; it can also be touched by kubectl, Helm, Argo CD, an HPA, admission webhooks, operators, and built-in controllers.
All of them can write to the same API object.
That raises a simple question: when several tools care about the same field, who owns it?

In a small cluster, this can look like an implementation detail.

In a real cluster, it decides whether an HPA keeps the replica count it just calculated, whether a Helm upgrade undoes a manual hotfix, or whether a GitOps controller reverts a change made by another controller.
This article explains why apply has to do more than “send this YAML to Kubernetes”.
It has to decide what changed, what should be preserved, and what another tool might already be managing.
To understand why this is hard, let’s start with what kubectl apply does today.

Link to original