Skip to main content
RunBook Academy

Git, CI/CD & GitOpsLXXIX · Sync StrategiesSyncStrategies

Replace, force, and server-side apply — the options and their consequences

Advanced⏱ ~25 mingitargocdflux

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

Not yet marked complete on this device.

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 classSSAReplace / force
Workload (Deployment, StatefulSet)truefalse
Service, Ingresstruefalse
DaemonSet, ClusterRoletruetrue if owned end-to-end
CRDtruefalse
ConfigMap, Secrettruefalse
ServiceAccount (operator-owned)truetrue 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:

  1. SSA with force: false (default). Conflicts surface; operator fields preserved. The right choice for shared resources.
  2. SSA with force: true or Replace=true. Conflicts resolved in the controller’s favour; operator fields overwritten. The right choice for controller-owned resources.
  3. 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

  1. ServerSideApply=true is the default. Every new Application runs with SSA enabled. Client-side apply is a legacy fallback.
  2. force: false is the default. Conflicts surface; the operator’s fields are preserved.
  3. force: true is enabled per resource, not per cluster. A cluster with mixed ownership has resources that need force and resources that do not.
  4. Replace=true is reserved for controller-owned resources. A Service or Ingress with Replace=true will 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

  1. 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?

  2. Q2. Replace=true and force=true in Flux have the same effect on a Kubernetes resource: both result in the controller winning every conflict.

  3. Q3. Name the two flags that resolve conflicts in favour of the controller, and identify the controller each belongs to.

  4. 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.