Skip to content

Handle server-side-apply conflicts safely

A server-side apply conflict means Kix is trying to change a field owned by another field manager. Identify that manager and decide who should own the field before forcing a deployment.

The example below starts with a Kix-managed Deployment. A separate manager named autoscaler then takes ownership of spec.replicas and changes it from two to three.

Reproduce the conflict without changing the cluster

Section titled “Reproduce the conflict without changing the cluster”

Run a reconciled API-server dry run:

Run in kix-examples/ Output excerpt
❱ kix deploy how-to-adoption-takeover --reconcile --dry-run -y
⚠ Deployment/legacy-web@takeover field-ownership conflict on Deployment/legacy-web@takeover: another field manager owns one or more fields kix is trying to set. Re-run with --force-conflicts to take ownership. Underlying error: ApiError: Apply failed with 1 conflict: conflict with "autoscaler": .spec.replicas: Conflict (ErrorResponse { status: "Failure", message: "Apply failed with 1 conflict: conflict with \"autoscaler\": .spec.replicas", reason: "Conflict", code: 409 })
✗ halt-on-first-failure: field-ownership conflict on Deployment/legacy-web@takeover: another field manager owns one or more fields kix is trying to set. Re-run with --force-conflicts to take ownership. Underlying error: ApiError: Apply failed with 1 conflict: conflict with "autoscaler": .spec.replicas: Conflict (ErrorResponse { status: "Failure", message: "Apply failed with 1 conflict: conflict with \"autoscaler\": .spec.replicas", reason: "Conflict", code: 409 })
Dry run complete: 0 created, 5 configured, 0 unchanged, 1 failed, 4 cancelled
(exit code: 1)

--reconcile makes Kix check every resource even when the activation itself has not changed. --dry-run asks the API server to validate the apply without persisting it.

Read the conflict message closely. It identifies three things:

  • the resource, Deployment/legacy-web@takeover;
  • the other field manager, autoscaler;
  • the overlapping field, .spec.replicas.

By default, the run stops after this conflict and ends with 1 failed, 4 cancelled. The cancelled resources have not been checked. To find conflicts on the remaining resources, run the dry run again with --on-error continue:

Run in kix-examples/
❱ kix deploy how-to-adoption-takeover --reconcile --dry-run -y --on-error continue

This still skips resources that depend on a failed resource, but continues checking independent branches of the dependency graph.

Fetch the resource with its managed fields:

❱ kubectl get deployment legacy-web -n takeover -o yaml --show-managed-fields

Find the named manager under metadata.managedFields, then determine what owns it. Common managers include an operator, an autoscaler, kubectl, Helm, and another deployment system.

Also inspect the current field value and the Kix definition. A conflict is not evidence that the other value is wrong.

If the other controller should continue managing the field, change the Kix package or its configuration so it no longer emits that field. When the package cannot omit it, because the field is required on create or the whole object comes from upstream, declare the field as ceded on the resource:

scope.mkResource {
# ...
ownership.cede = [ "spec.replicas" ];
}

Kix stamps the paths as kix.run/cede-fields. When a later apply conflicts only on those paths, deploy retries without them and the other manager keeps the field; a conflict anywhere else still fails. See Cede fields to a controller. Choose one manager either way, rather than letting both repeatedly take ownership from each other.

If Kix should manage the field, first disable or reconfigure the other manager for this resource. Otherwise the conflict will return after both systems reconcile again.

Once Kix is the intended owner, add --force-conflicts to the same dry run:

Run in kix-examples/ Output excerpt
❱ kix deploy how-to-adoption-takeover --reconcile --dry-run --force-conflicts -y
~ Deployment/legacy-web@takeover configured
✔ Deployment/legacy-web@takeover ready
Dry run complete: 0 created, 10 configured, 0 unchanged, 0 failed

This confirms that the API server accepts the transfer while still writing nothing. Review the rest of the plan before continuing.

Run the reconciled deployment with the same flag:

Run in kix-examples/ Output excerpt
❱ kix deploy how-to-adoption-takeover --reconcile --force-conflicts -y
~ Deployment/legacy-web@takeover configured
✔ Deployment/legacy-web@takeover ready
• activation 'how-to-adoption-takeover-nphkg40wdk30' → Active
Deploy complete: 0 created, 10 configured, 0 unchanged, 0 failed

The successful apply transfers the overlapping field to the kix field manager and restores the replica count from the Kix definition.

Return to ordinary deployments after resolving the conflict. Keeping --force-conflicts in routine commands can hide a new ownership dispute that needs a separate decision.