Skip to content
kix /docs
Install the CLI

How-to guide Operate a cluster

Preview changes with kix plan and kix diff

Preview a deploy with a server-side dry run, or compare rendered manifests with live Kubernetes resources using read-only access.

Kix has two previews, and neither changes the cluster:

  • kix plan runs the deploy with every write sent as a server-side dry run. Use it to preview a deploy you are about to run. It needs the same permissions as the deploy.
  • kix diff only reads. Use it where the job has read-only credentials, or no cluster access at all, such as a pull request check.

Run the plan with the flags you will deploy with:

kix-examples/
❱ kix plan how-to-application --prune

The output is the package-grouped plan kix deploy prints before it asks for confirmation. Each field change is what the apiserver says the apply would produce. After the packages, the plan lists anything the deploy would stop on: a field another manager owns, a webhook or admission policy denial, a validation error, an immutable field, or an RBAC refusal. Every refusal is listed, so you can fix them all before deploying.

The plan exits 0 when the deploy would change and delete nothing, 2 when it would change or delete something, and 3 when it would stop. In CI, treat 0 and 2 as success and block on 3, a deploy the cluster would refuse; kix plan has a script that does this.

Objects whose CRD or namespace the same deploy creates cannot be checked by a dry run. The plan lists them under “Not checked by the dry run” with the changes from comparing builds. On a cluster’s first deploy most objects are in this list.

Compare desired and live resources with kix diff

Section titled “Compare desired and live resources with kix diff”

Start from a cluster that matches what is deployed. Kix compares only the fields it applied, so an unchanged cluster reports nothing and exits 0:

kix-examples/
❱ kix diff how-to-application
⠁ Fetching live cluster state...                                                Discovering API resources...
Fetching managed resources (60 resource types)...
Fetched 16 resources across 60 resource types
3 packages: 3 unchanged, 0 changed, 0 added, 0 removed
No differences found.

Now change the production message in the cluster definition and run the same command:

kix-examples/
❱ kix diff how-to-application
⠁ Fetching live cluster state...                                                Discovering API resources...
Fetching managed resources (60 resource types)...
Fetched 16 resources across 60 resource types
3 packages: 2 unchanged, 1 changed, 0 added, 0 removed

  how-to-app
    ~ production 1.0.0 (1 changed, 4 dep-affected)
      ~ ConfigMap/production@how-to-app
          ~ $.data.index.html:
              --- old
              +++ new
              @@ -1 +1 @@
              -Hello from production
              +Updated production message
      ~ Deployment/production@how-to-app (via dependency)
      ~ Job/production-health@how-to-app (via dependency)
      ~ PackageInstance/production@how-to-app (via dependency)
      ~ Service/production@how-to-app (via dependency)

  Activation: how-to-application-gb5d6ry45b5l -> how-to-application-s3ca8rcni68g
(exit code: 2)

The report names the ConfigMap you edited and shows the changed value. The resources marked “via dependency” have no content change of their own. Their identity hash moves because they depend on the ConfigMap, so Kix will re-stamp them on the next deploy.

The report is grouped by package, and each line tells you something different:

  • A package marked added or removed appears on only one side.
  • A resource marked ~ has a content change, shown as a diff of the fields that differ.
  • A resource marked “via dependency” has no content change of its own. Only its identity hash moved, because something it depends on changed.
  • A cluster-level group collects resources that belong to no package, such as namespaces and custom resource definitions.
  • An activation line names the deployment record this change would create.

Read the packages you edited first, then the dependent resources, which tell you how far the rollout reaches. A non-empty diff exits with status 2, which lets CI tell a change from a clean comparison.

When the deployed build is on this machine, a field someone changed on the cluster is tagged (reverts drift) when the deploy puts it back, and a Drift section at the end names the field manager that changed it. When there are changes, the last line reminds you that kix diff cannot see admission webhooks, field ownership conflicts, or deploy refusals. Run kix plan for those.

By default, Kix evaluates the current directory and reads the current kubectl context. Set either input explicitly when needed:

❱ kix diff how-to-application --flake ./infrastructure --context kind-kix-demo

The flake supplies the desired manifests. The Kubernetes context supplies the live manifests being compared.

Select Markdown output when attaching the result to a pull request or change review:

kix-examples/ live capture
❱ kix diff how-to-application --output markdown
Show outputHide output · 28 lines
⠁ Fetching live cluster state...                                                Discovering API resources...
Fetching managed resources (60 resource types)...
Fetched 16 resources across 60 resource types
### kix diff

**Plan:** 1 updated, 0 added, 0 removed, 2 unchanged
**Resources:** 1 with real content changes, 4 dep-affected (hash bump only)

#### Changed packages

<details>
<summary><code>how-to-app/production</code> 1.0.0 (1 changed, 4 dep-affected)</summary>

**Modified resources:**

- `ConfigMap/production@how-to-app` (1 diffs)

**Dep-affected (hash bump only, no content change):**

- `Deployment/production@how-to-app`
- `Job/production-health@how-to-app`
- `PackageInstance/production@how-to-app`
- `Service/production@how-to-app`

</details>

**Activation:** `how-to-application-gb5d6ry45b5l` → `how-to-application-s3ca8rcni68g`
(exit code: 2)

Redirect it into a file for the job to publish:

kix-examples/
❱ kix diff how-to-application --output markdown > kix-diff.md

kix plan --output markdown produces the same layout with the write-path checks added, for a job that has the deployer’s credentials.

Use JSON when another program will process the result:

kix-examples/
❱ kix diff how-to-application --output json > kix-diff.json

kix diff and kix plan print the same JSON document, so one consumer can read either. The checks a read-only diff cannot make read { "status": "not_checked", "reason": "read_only" }. By default the JSON names each changed resource and counts its changed fields. Add --detail fields to list each field change, old to new:

kix-examples/
❱ kix diff how-to-application --output json --detail fields > kix-diff.json
{ "path": "$.spec.replicas", "change": "changed", "old": 2, "new": 3, "labels": ["intended"] }

change is added, removed, or changed. labels is present when the deployed build is on this machine. Add --detail manifests to also include, under manifests on each package, the old and new manifest of every resource that changes, is added, or is removed, for a tool that shows a line-by-line diff. In kix plan the two manifests of an object the cluster checked are the live object and the cluster’s dry-run result:

kix-examples/
❱ kix diff how-to-application --output json --detail manifests > kix-diff.json

At both levels, Secret values and hashes of them read (secret, not shown), or (secret, changed, not shown) on the new side when the value changes. A field value larger than 16 KiB, or a manifest larger than 256 KiB, reads (N bytes, not shown). A password written as a literal container env value or into a ConfigMap is shown as it is, so keep secrets in Secrets.

After deploying the intended change, run kix diff or kix plan again. A clean result confirms that Kix does not currently plan another content change.

kix diff compares the fields Kix applied. Use kix drift when you need to know who else owns a field and what they changed.