Skip to content

Kix is a Kubernetes compiler.

Compile your whole cluster from one definition.
Make deploys boring.

The CLI is available in early access. Deploy from your laptop or CI; Kix requires no controller in the cluster.

clusters/shop.nix

      
      { kix, packages }:
    
      
      kix.buildCluster {
    
      
        name = "shop";
    
      
        modules = [
    
      
          kix.flavors.eks
    
      
          {
    
      
            availablePackages = packages;
    
      
            instances.shop.db.package = packages.cnpg-cluster;
    
      
      
    
      
            # web asks for a Postgres. The cluster provides one.
    
      
            instances.shop.web = {
    
      
              package = import ./web.nix;
    
      
              config.replicas = 3;
    
      
            };
    
      
          }
    
      
        ];
    
      
      }
    
kix check shop
ToolResultDetails
eval pass 37 manifests evaluated
scorecard pass 0 errors, 2 warnings, 0 info

Why we built Kix

Our team managed Kubernetes with YAML. We added templates, overlays, controllers to sync changes from Git, and policies to catch mistakes. Each tool addressed a real need, but together they made the whole system substantially more complex. We wanted to go back to first principles and see what would happen if we described the cluster as a whole.

  1. 05 policy engines that check YAML afterwards
  2. 04 controllers that sync YAML from git
  3. 03 overlays that patch YAML
  4. 02 templates that generate YAML
  5. 01 YAML
  6. Kubernetes resources

What we considered

We considered describing Kubernetes with a general-purpose language, but couldn’t see a clean way to express the cluster as a whole.

What changed

As agents began writing more infrastructure code, the need for precise error messages grew. Whether a person or an agent makes a change, they need to know which resource is wrong and why.

What we built

So we built Kix as a compiler for the whole cluster definition. It can check the resources together, work out their dependencies, and use that information to plan and carry out a deployment.

A definition for the whole cluster

The compiler needs to know how resources connect, so a Kix definition uses references where one resource depends on another. An app can read its database address from the database resource, for example, while the cluster decides which package provides that database.

a string
DB_HOST = "db.shop.svc.cluster.local";

This address is a string, so Kix cannot tell which Service it refers to.

a reference
DB_HOST = db.out.fqdn;

With a reference, Kix can follow the connection from web to db. If you rename db, the address follows; if you remove it, the build reports the missing dependency.

Once Kix has those connections, it can derive:

  • Deploy order Resources wait for their dependencies to be ready.
  • RBAC Roles and bindings built from the access each package declares.
  • Monitoring config When a Prometheus-compatible stack is in the cluster.
  • Routes Ingress or Gateway API, from the Service.
  • Spread and PDBs Pod spread and PodDisruptionBudgets.
  • Clean-up Resources that are no longer defined.
  • Network policies Derived from real dependencies when enabled, on Cilium.

A. check the definition

Kix checks the complete definition before applying it. If a Service points to no workload, for example, the error identifies the Service and the selector to check. Other mistakes, such as a Deployment selector drifting from its pod labels, are prevented by how Kix builds the resources.

The scorecard also checks security, reliability, and governance. Its findings are warnings by default, and you can choose which rules should stop a build.

service.yaml
kind: Servicemetadata:  name: web  namespace: shopspec:  selector:    app.kubernetes.io/name: shop-wbe  ports:    - port: 80

The Service has no endpoints, so traffic cannot reach the app.

kix check shop
✗ Service shop/web selector {"app.kubernetes.io/name":"shop-wbe"} matches no workload pod template in namespace shop. Check the selector against the intended workload's pod labels
# kix deploy stops at the same error
Everyday mistakes, what Kubernetes does with them, and what Kix reports
The everyday mistake What Kubernetes does What Kix says
The same environment variable defined twice, usually from a copied block. Accepts it, warns, and the last value wins.
✗ [web] Container 'web' has duplicate env var name(s): [DATABASE_URL].
The app reads a Secret key that doesn't exist: password, when the key is pass. The pod fails to start.
✗ secret "db" does not declare key "password" declared keys: pass
A migration Job reuses the app's labels. The Service sends real user traffic to the Job's pod for as long as it runs.
✗ Job shop/migrate pod labels {"app.kubernetes.io/instance":"web","app.kubernetes.io/name":"web"} satisfy Service shop/web selector {"app.kubernetes.io/instance":"web","app.kubernetes.io/name":"web"}; the Service would route traffic to the Job pod
A workload names a PriorityClass that doesn't exist. The workload applies, then every pod is rejected at admission.
✗ Deployment shop/web sets priorityClassName 'critical', which no resource in this cluster creates. Every pod of this workload would be rejected at admission. Add the PriorityClass, or declare it as externally managed with an import or kix.untrackedRef
A PodDisruptionBudget whose selector matches nothing. It protects nothing, and you find out during a node drain.
✗ PodDisruptionBudget shop/web selector {"app":"wbe"} matches no workload pod template in namespace shop. Check the selector against the intended workload's pod labels
type: LoadBalancer on a cluster with no load balancer provider. The external IP sits at <pending>.
✗ [web] Service 'web' uses type=LoadBalancer but no load balancer provider is configured (cluster.roles.loadBalancer.binding = "absent", no provider instance). Use type=ClusterIP or NodePort, add a provider instance, or configure cluster.roles.loadBalancer.binding to match your environment.

B. plan a change

Before deploying, kix diff compares the compiled definition with the live cluster and groups the changes by package. You can also post the plan to a pull request so the review happens alongside the change.

When you deploy, Kix applies resources in dependency order and waits for each step to become ready before moving on.

$ kix diff shop12 packages: 9 unchanged, 2 changed, 1 added, 0 removed  monitoring    ~ grafana 11.2.0 (1 changed, 1 dep-affected)      ~ Deployment/grafana@monitoring          ~ $.spec.replicas:              old: 1              new: 2      ~ PackageInstance/grafana@monitoring (via dependency)  shop    + valkey 8.0.2 (4 resources)    ~ web 1.4.2 -> 1.5.0 (1 changed, 2 dep-affected)      ~ Deployment/web@shop          ~ $.spec.template.spec.containers[web].image:              old: "ghcr.io/acme/web:1.4.2"              new: "ghcr.io/acme/web:1.5.0"      ~ HorizontalPodAutoscaler/web@shop (via dependency)      ~ PackageInstance/web@shop (via dependency)  Activation: shop-ll6g0k5f2kqm -> shop-6w9czkc51502

$ kix deploy shop dependency order

  1. wave 1 Secret/dbConfigMap/web
  2. wave 2 Cluster/db
  3. wave 3 Service/webDeployment/web
  4. wave 4 HTTPRoute/web
kix rollback
Reapplies a previous deploy from the local Nix store. kix gc keeps at least the 10 newest records.
kix drift
Reports Kix-managed resources whose applied fields changed since the last deploy, and which tool changed them.
--prune
Removes resources no longer defined, except Namespaces and CRDs. Without --prune, the plan lists them and leaves them in place.
kix snapshot
Saves the Kix-managed resources of a live cluster, so kix diff --from can compare against them later.

C. see what was deployed

Kix records a receipt for each deployment on the cluster, so you can trace what is running back to the inputs and images that produced it.

  • during an incident

    “What changed since yesterday?”

    Each receipt records the commit, whether the working tree was clean, the tool versions, and the container images.

  • during a release

    “Did the new build actually go out?”

    Image versions and digests live in a committed file, which lets you trace a release to a commit. kix pin check --source-rev fails when a pinned image was built from a different commit.

  • during a rollback

    “Put back exactly what ran on Tuesday.”

    kix rollback reapplies a previous deploy's build from the Nix store, exactly as it ran.

  • during an audit

    “Show me evidence.”

    kix compliance produces a CycloneDX SBOM, an unsigned SLSA provenance statement, and an audit bundle mapped to SOC 2, ISO 27001, DORA, or NIS2. An auditor can use these artifacts as evidence when assessing compliance.

KIX DEPLOY RECEIPT kix.run/receipt/v1

cluster
shop
deployed
2026-09-09 10:47
commit
c1b8d5e
working tree
clean
kix
0.1.0
arch
aarch64 / macos

IMAGES

docker.io/nginxinc/ nginx-unprivileged:1.28-alpine @sha256:7377697a…


Stored on the cluster after each deploy. Manifests stay out of the receipt because they can contain secrets.

D. use packages across clusters

Packages respond to the needs of each cluster. The same package can run one replica on a single-node development cluster, then spread replicas across zones on a production cluster configured for high availability.

packages.cnpg-cluster
  • kind · 1 node cluster availability: none
    1 instance, no high availability
  • eks · 3 zones cluster availability: zone
    3 instances spread across zones for high availability

Cluster profiles included

  • EKS
  • GKE
  • GKE Autopilot
  • AKS
  • OpenShift
  • k3s
  • RKE2
  • Talos
  • kubeadm
  • kind

Profiles describe cluster capabilities. You can also use any conformant Kubernetes cluster.

Apps depend on an interface. Clusters choose the provider.

Availability is set for the cluster, and packages adapt to it. Separately, the cluster can choose which package provides an interface such as ClickHouse, without changing the apps that use it.

# single-node ClickHouse provider- instances.data.olap.package = packages.clickhouse;# operator-managed ClickHouse provider (with its operator and Keeper)+ instances.data.olap.package = packages.clickhouse-cluster;

Packages for the software you run

The library covers databases, observability, networking, and applications, with more packages added all the time.

Growing the library

We use an established agent-assisted process to build new packages quickly and test each one against a range of test clusters we operate.

Helm charts work too

You can use existing Helm charts with Kix. They let you bring in software you already use, though they do not provide every benefit of a native Kix package.

Compiler feedback for people and agents

An agent can edit a Kix definition and use compiler errors to revise it. The resulting plan gives you a manageable view of the proposed change before deployment.

  • Agents write infrastructure code now.

    Agents already write Kubernetes configuration for many teams, so they need the same checks that a person would use.

  • An agent needs fast, exact feedback.

    Kix identifies the affected resource and explains the error, giving the agent something specific to correct.

  • You need something small enough to review.

    You can review the results of kix check and the package-level plan from kix diff before approving a change.

the loop, for agents and people alike
  1. agent or person edit the cluster definition
  2. kix $ kix check Names the resource and explains what to fix. back to the definition until it builds
  3. kix $ kix diff The plan, grouped by package.
  4. you review the plan Review the changes by package on the pull request.
  5. kix $ kix deploy In dependency order, waiting on readiness.

Why the Nix language?

Kix definitions are written in the Nix language, which has been used for reproducible builds for over 20 years. Kix uses the language rather than NixOS, and it runs on any machine with Nix installed. It relies on four of the language's features directly.

  • String context

    Nix records which values a string was built from. Kix reads those records to find every dependency, including addresses used inside a connection string or a ConfigMap.

  • Pure evaluation

    A Nix expression cannot change anything outside the evaluation. Evaluating a definition never calls the Kubernetes API, so kix check and kix build run without cluster credentials.

  • Locked inputs

    flake.lock pins Kix and every package by hash, and image pins are committed with the definition. The same commit builds the same resources.

  • The module system

    Package options have types and defaults, and settings from flavors and clusters are merged by the same rules everywhere.

Deploy from CI or your laptop.

The cluster definition and image versions live in Git. Kix can deploy a commit from your laptop or any CI system and write a receipt to the cluster afterward.

An optional in-cluster operator is in development.

ci-bot commented on pull request #214

kix diff

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

Added packages

  • shop/valkey 8.0.2 (4 resources)

Changed packages

  • monitoring/grafana 11.2.0 (1 changed, 1 dep-affected)
  • shop/web 1.4.2 → 1.5.0 (1 changed, 2 dep-affected)

Activation: shop-ll6g0k5f2kqm to shop-6w9czkc51502

  1. pull request open a change
  2. kix check fails the build on errors
  3. kix diff -o markdown plan posted on the PR
  4. merge a release is a commit
  5. kix deploy ordered, waits on readiness
  6. receipt written to the cluster

Questions before you try Kix

Kix produces standard Kubernetes resources, and the CLI fits into an existing workflow. These are the details people usually ask about before trying it.

Is it still standard Kubernetes underneath?
Yes. Kix produces ordinary Kubernetes resources that kubectl, dashboards, operators, and security tools can inspect.
What if you stop using Kix?
kix export writes the cluster as plain Kubernetes YAML. If you stop using Kix, you can keep managing those resources with standard Kubernetes tools.
Can Kix use existing Helm charts?
Yes. You can use existing Helm charts with Kix, although they do not provide every benefit of a native Kix package.
What if Kix doesn't model something I need?
Define any Kubernetes resource or field directly in the cluster definition.
Does anything need to run in my cluster?
Nothing runs in the cluster: the CLI deploys from your laptop or CI. Kix adds two CRDs and records each deploy, with its receipt, as an Activation resource.
Does Kix block insecure configs?
Security checks run on every build. Scorecard findings are warnings by default; choose which rules fail the build.
Which packages exist?
The library covers databases, observability, networking, and applications, with more added regularly. See the Packages section →

Core team

Built from experience running clusters.

  • Luca Dombetzki

    Luca Dombetzki

    Built TUM.ai, Europe's largest AI student initiative. Senior Machine Learning Engineer at Valinor Discovery, virtual patient models for medicine.

    LinkedIn
  • Adam Charnock

    Adam Charnock

    20 years in production, founder of Lithus, managed bare-metal Kubernetes in the EU. Previously of Twitter, off-grid ISP founder, and house builder.

    LinkedIn

Compile your first cluster.

Install the CLI, compile an example, and review the plan before deploying.