Git, CI/CD & GitOpsLXXIX · Sync StrategiesSyncStrategies
Replace, force, and server-side apply — the options and their consequences
What you'll learn
- Describe what server-side apply does and why it is the default for production
- Identify when the Replace sync option in Argo CD is right and when it is wrong
- Identify when the force field in Flux is right and when it is wrong
- Use the right combination of SSA, Replace, and force for shared-ownership resources
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
Most Kubernetes resources are not owned by a single actor. A
Service has fields the controller owns (spec.selector,
spec.ports), fields the operator might want to set
(metadata.labels for a service mesh), and fields that change
automatically (status.conditions). Server-side apply (SSA) is
the mechanism that lets the GitOps controller own its fields
without trampling the operator’s. The Replace option in Argo CD
and the force field in Flux are the levers that bypass that
ownership model when the controller must own everything.
What server-side apply does
Server-side apply tracks field-level ownership. Each field on
the resource has a managedFields entry that records which
actor last applied it. When two actors apply the same field,
the conflict resolution policy decides what happens.
syncOptions:
- ServerSideApply=true
flowchart LR
A["Controller applies"] --> S["Server-side apply"]
O["Operator applies"] --> S
S --> M["managedFields tracks per-field ownership"]
M -->|"no conflict"| OK["Resource updated"]
M -->|"conflict, force=false"| C["Conflict, Ready=False"]
M -->|"conflict, force=true"| W["Controller wins"]
The default behaviour is “no conflict, both succeed”. A
conflict surfaces as a Ready=False condition on the
Kustomization; the operator’s field is preserved. With
force=true, the controller wins the conflict, and the
operator’s field is overwritten.
Why SSA is the production default
Client-side apply is kubectl apply’s original model. It
tracks ownership only via the
kubectl.kubernetes.io/last-applied-configuration annotation,
which is a single string per resource - not a per-field
ownership record. Two actors applying the same resource
race; the last writer wins. The annotation is also lossy: a
field the client does not know about is not in the annotation
and is “owned” by the cluster’s default behaviour.
SSA replaces this with a proper per-field ownership record. The GitOps controller’s fields are tracked under the controller’s identity; the operator’s fields are tracked under the operator’s identity; both can coexist on the same resource. The production default is SSA; client-side apply is a fallback for legacy resources.
The Replace sync option
Argo CD’s Replace sync option is the destructive lever. When
enabled, Argo CD does not apply the resource; it deletes and
recreates it. The flag:
syncOptions:
- Replace=true
The controller’s apply becomes a replace: the resource is
deleted with a foreground propagation, then recreated with
the desired state. The result is a fresh resource with a new
resourceVersion and no preserved fields.
The force field in Flux
Flux’s equivalent is spec.force on a Kustomization:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: payment-api
spec:
interval: 10m0s
sourceRef:
kind: GitRepository
name: apps
path: ./clusters/production/payment-api
prune: true
force: true
force: true makes the controller win every SSA conflict.
With force: false (the default), a conflict surfaces as
Ready=False and the operator’s field is preserved. The two
values are the SSA conflict-resolution policy.
The Flux force field operates on SSA conflicts; Argo CD’s
Replace operates at the resource level. The two are not the
same mechanism but they solve the same problem - the
controller must own every field - in two different layers.
When each option is right
The decision matrix:
| Resource class | SSA | Replace / force |
|---|---|---|
| Workload (Deployment, StatefulSet) | true | false |
| Service, Ingress | true | false |
| DaemonSet, ClusterRole | true | true if owned end-to-end |
| CRD | true | false |
| ConfigMap, Secret | true | false |
| ServiceAccount (operator-owned) | true | true if controller owns SA |
The rule: use SSA always; enable Replace or force only for
resources the controller owns end-to-end. A resource with
shared ownership under Replace=true or force: true loses
the operator’s fields on every apply.
The consequences
The three options in increasing order of destructiveness:
- SSA with
force: false(default). Conflicts surface; operator fields preserved. The right choice for shared resources. - SSA with
force: trueorReplace=true. Conflicts resolved in the controller’s favour; operator fields overwritten. The right choice for controller-owned resources. - Replace=true in Argo CD. Resource deleted and recreated. The strongest reset; also the most destructive. Use only when the resource is fully owned by the controller and the recreation cost is acceptable.
Production discipline
ServerSideApply=trueis the default. Every new Application runs with SSA enabled. Client-side apply is a legacy fallback.force: falseis the default. Conflicts surface; the operator’s fields are preserved.force: trueis enabled per resource, not per cluster. A cluster with mixed ownership has resources that need force and resources that do not.Replace=trueis reserved for controller-owned resources. A Service or Ingress withReplace=truewill lose operator labels on every apply.
Cross-course references
- Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXVII-04 (Sync policies and windows) is the policy frame; Part LXXVIII-03 (Kustomization controller) covers the Flux apply model this lesson deepens.
- Kubernetes for Production Sysadmins - Parts VII-VIII (Workloads) cover the resource classes SSA and Replace operate on; Part XVI (Operators and Controllers) covers the reconcile loop that uses these flags.
Quiz
Knowledge check · 4 questions
Q1. An operator adds a label to a Service that the GitOps controller manages. The controller is configured with ServerSideApply=true and force=false. What happens on the next reconcile?
Q2. Replace=true and force=true in Flux have the same effect on a Kubernetes resource: both result in the controller winning every conflict.
Q3. Name the two flags that resolve conflicts in favour of the controller, and identify the controller each belongs to.
Q4. Diagnose why the operator's labels disappeared after the controller's latest sync and recommend a fix.
A team runs a Service with ServerSideApply=true and Replace=true in Argo CD. The on-call operator adds a label `service-mesh: enabled` to the Service during an incident. The next automated sync applies; the Service is deleted and recreated by Argo CD. The operator's label is gone. The service mesh silently stops routing the Service. The team is unaware until the next incident postmortem.
Passing score: 75%. Answers are checked in this browser.