kubectl apply --server-side: Field Ownership and Conflicts
kubectl apply --server-side performs Server-Side Apply: the API server merges your manifest and records which manager owns each field.
Server-Side Apply makes ownership explicit, so two controllers (or two pipelines) editing the same object surface a real conflict instead of silently clobbering each other.
What it does
With --server-side, the merge happens on the API server rather than from the local last-applied annotation. Each field is tagged with a field manager. If your apply would overwrite a field owned by a different manager, the server returns a conflict instead of taking the field, unless you pass --force-conflicts.
Common usage
kubectl apply --server-side -f deploy.yaml
# claim conflicting fields explicitly
kubectl apply --server-side --force-conflicts -f deploy.yaml
# name the manager so ownership is attributable
kubectl apply --server-side --field-manager=ci-pipeline -f deploy.yamlOptions
| Flag | What it does |
|---|---|
| --server-side | Use Server-Side Apply (merge on the API server) |
| --force-conflicts | Take ownership of fields currently owned by others |
| --field-manager=<name> | Identify the manager recorded for owned fields |
| --dry-run=server | Run the apply on the server without persisting |
| -f, --filename | Manifest file or directory to apply |
In CI
Set a stable --field-manager per pipeline so conflicts are attributable. When an HPA or another controller owns spec.replicas, drop replicas from your manifest rather than fighting with --force-conflicts, which can fight the autoscaler on every deploy.
Common errors in CI
"Apply failed with N conflicts: conflicts with \"<manager>\" using ...: .spec.replicas" means another manager owns that field; either remove it from your manifest or add --force-conflicts if you really intend to take it. "error: --server-side and --dry-run=client are incompatible" means you combined client dry-run with server-side; use --dry-run=server. Mixing client-side and server-side applies on the same object can leave a stale last-applied annotation.
Using this in CI
A runner has no kubeconfig, no cached context, and no interactive auth. Every kubectl invocation in CI needs the context supplied explicitly, and most confusing CI failures here are the command running against the wrong cluster or no cluster at all.
# never rely on the ambient context on a runner
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods
# confirm what you are actually connected to before mutating anything
kubectl config current-context
kubectl cluster-info
# fail fast instead of hanging on an unreachable API server
kubectl --request-timeout=30s get nodes