Skip to content
kix /docs
Install the CLI

Explanation

Server-side apply, field ownership, and drift

How Kubernetes field managers let Kix distinguish desired fields from controller-owned state, and why conflicts require an ownership decision.

Kix applies resources with Kubernetes server-side apply and the field manager name kix. The API server records which manager owns each field in metadata.managedFields. That record lets Kix coexist with controllers that change the same object without treating every controller-written value as part of Kix’s desired state.

This page is hand-maintained. Check kix/lib/core/resource.nix and cli/kix/src/cluster/apply.rs when in doubt.

Several managers can own different parts of one Kubernetes object. Kix might own a Deployment’s container image, labels, and resource limits while an HPA owns spec.replicas. Kubernetes does not require one tool to own the whole Deployment.

Server-side apply compares the fields a manager sends with the ownership record. When Kix tries to change a field another manager owns, the API server returns a conflict instead of choosing a winner. This is useful evidence: the desired manifest and the live controller disagree about responsibility for a specific field.

Fields that Kix never sent are outside its field set. Status, API defaults, and values added by controllers therefore do not conflict merely because they appear on the live object.

The resolution depends on which manager should control the field.

If Kix should own it, stop or reconfigure the other writer, then use --force-conflicts for the ownership transfer. A forced server-side apply takes the conflicting fields for Kix and writes the values in the manifest. Leaving that flag on every deploy is risky because a new, unrelated ownership dispute will also be forced.

If the other manager should own it, the package should stop sending the field. Some resources cannot omit a field on creation, and some packages carry a complete upstream manifest. For those cases, the package can declare the field under ownership.cede. Kix sends it when creating the object. On a later conflict confined to the declared paths, Kix retries once without those fields, allowing the other manager to retain them.

This makes ceding a narrow package contract. The package author knows that a particular controller is expected to take spec.replicas or a CRD’s conversion fields. A conflict from an unexpected manager or on an undeclared path still stops the deployment. Automatically forcing or discarding every conflict would erase that distinction.

After an apply, Kix hashes the live fields that its field manager owns and stores the result in kix.run/applied-hash. kix drift later selects the same owned fields, hashes their current values, and compares the result with the stamp.

This means drift is narrower than a whole-object comparison:

  • a controller updating status is not drift when Kix does not own status;
  • an API server default is not drift when Kix never sent the field;
  • changing a field Kix owns is drift, even when the change came through a subresource such as /scale;
  • a field Kix successfully ceded falls outside Kix’s owned field set and is no longer judged as Kix drift.

kix diff applies the same boundary when it compares a build with the live cluster. It extracts Kix-owned fields from live objects so defaults and controller state do not fill the report with irrelevant differences.

Field ownership answers who is responsible for a value. Readiness answers whether the resulting object is usable. A resource can have no ownership conflict and still fail its readiness check, so deployment handles the two separately.