# Server-side apply and drift

In a live Kubernetes cluster, your deployment tool is rarely the only thing modifying objects. HorizontalPodAutoscalers adjust `spec.replicas`, operators inject webhook conversion blocks into CRDs, and the Kubernetes API server fills in default values on every pod spec. Kix uses Kubernetes **Server-Side Apply** to track exactly which fields Kix owns, so it can detect real configuration drift without fighting in-cluster controllers or flagging harmless API server defaults.

:::note[Helpful background]
- [05. The basic graph and deploy loop](/docs/tutorials/05-basic-graph-and-deploy-loop/): See how `kix diff`, `kix deploy`, and `kix drift` work against a live local cluster.
- [Annotations, CRDs, and receipts](/docs/reference/annotations-crds-and-receipts/): Reference for the `kix.run/applied-hash` annotation and the `Activation` receipt schema.
- [Kubernetes Server-Side Apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/): How the Kubernetes API server tracks per-field ownership in `metadata.managedFields`.
:::

## Per-field ownership in Server-Side Apply

When `kix deploy` applies a manifest, it uses Kubernetes Server-Side Apply with the field manager name `kix`. Instead of storing a giant "last-applied-configuration" JSON blob in an annotation, the Kubernetes API server records which manager owns each individual field inside `metadata.managedFields`.

Because ownership is tracked per field rather than per object, Kix and an in-cluster controller can safely co-own the same Deployment:

- Kix can own the Deployment's container image, environment variables, and resource requests.
- A HorizontalPodAutoscaler can own `spec.replicas` on that same Deployment.
- Fields that Kix never included in its manifest (such as `status` or API server defaults) stay outside Kix's owned field set.

When Kix tries to change a field that another manager currently owns, the Kubernetes API server returns a **field ownership conflict** instead of silently overwriting the value. That conflict is a useful safety check: it tells you that your Nix manifest and a live controller (or a manual `kubectl edit`) are both trying to control the same setting.

## Two ways to resolve an ownership conflict

How you resolve a conflict depends on who *should* control the field going forward:

1. **Kix should own the field:** If someone ran a one-off `kubectl edit` or you are migrating a resource from Helm to Kix, use `--force-conflicts` on `kix deploy` for that transition. A forced Server-Side Apply transfers ownership of the conflicting fields to `kix` and writes the values from your build. Avoid leaving `--force-conflicts` enabled on every routine deploy, because it would also steamroll legitimate conflicts from controllers.
2. **A controller should own the field (`ownership.cede`):** Sometimes a resource must include an initial value when it is first created, even though an in-cluster controller will take over that field afterward (for example, `spec.replicas` on an autoscaled workload, or webhook `spec.conversion` fields on a CRD). In those cases, the package declares the field path under `ownership.cede` on `scope.mkResource`. Kix sends the field when creating the object, and if a later apply conflicts *only* on declared ceded paths, Kix automatically retries without those fields so the controller keeps ownership.

Making `ownership.cede` an explicit list of field paths keeps conflicts honest: expected controller takeovers happen smoothly, while an unexpected conflict on any other field still stops the deployment and asks you to decide.

## Drift detection uses the same ownership boundary

Most diff and drift tools compare your desired YAML against the entire live object returned by the cluster, which creates constant noise whenever controllers update status fields or the API server adds default fields.

Kix uses the `kix` field-ownership entries in `metadata.managedFields` across both `kix drift` and `kix diff`:

- Right after `kix deploy` applies a resource, Kix extracts the live fields owned by `kix`, hashes them, and writes that digest to the `kix.run/applied-hash` annotation.
- When you run `kix drift`, Kix reads the live object, selects the fields owned by `kix`, hashes their current values, and compares them to `kix.run/applied-hash`. If a controller updates `status`, or if a field was cleanly ceded via `ownership.cede`, Kix ignores it. If someone changes a Kix-owned field out-of-band (even through a subresource like `kubectl scale`), `kix drift` flags the exact field that changed.
- When you run `kix diff` against a live cluster, Kix compares the desired build against the cluster's state: resources whose `kix.run/identity-hash` matches the build are treated as unchanged by the build, changed resources are compared against their `kix`-owned projection from `metadata.managedFields`, and a separate drift section checks `kix.run/applied-hash` and `metadata.managedFields` to report out-of-band edits and the field manager that made them.

## Why deploy inputs are committed

Just as Kix avoids hidden state in the cluster, it avoids hidden inputs at deploy time. Every input that affects a compiled manifest, including frequently updated values like container image digests, lives in a committed file in your repository and is read during evaluation:

- **Content-addressed identity hashes:** During evaluation, Kix computes each resource's `kix.run/identity-hash` from its serialized JSON in Kix's local store, and `kix deploy` uses those hashes (recorded in the cluster's `Activation` resource) to determine which objects changed since the last deployment. Reading all inputs during evaluation guarantees that the hashes in your build match the manifests applied to the cluster.
- **Committed JSON image pins (`pins.json`):** To make automated image updates easy without letting a CI bot rewrite Nix code, Kix stores image versions in a structured `pins.json` file managed by `kix pin`. Running `kix pin set <key> --tag <tag>` queries the container registry, records the immutable `sha256` digest alongside the tag, and writes the update to `pins.json`.
- **Verifiable deploy receipts (`status.receipt`):** When `kix deploy` finishes applying an `Activation`, it records a deploy receipt on the `Activation` status subresource containing the activation hash, timestamp, Git commit, locked flake inputs, and container image digests. Because every input was committed before evaluation, `kix compliance attest --from-cluster` can generate a verifiable SLSA provenance statement directly from the live cluster receipt.

:::tip[Resolve a conflict or promote an image]
Use [Handle server-side-apply conflicts safely](/docs/how-to/adopt-existing-resources/handle-server-side-apply-conflicts-safely/) when Kix should take ownership, [Cede fields to a controller](/docs/how-to/adopt-existing-resources/cede-fields-to-a-controller/) when another manager should keep a field, or [Create a committed image pin file](/docs/how-to/promote-images/create-a-committed-image-pin-file/) to manage image digests with `kix pin`.
:::

:::note[Reference]
See [Annotations, CRDs, and receipts](/docs/reference/annotations-crds-and-receipts/) for the `kix.run/applied-hash` annotation and `Activation` receipt schema.
:::