Skip to content
kix /docs
Install the CLI

Tutorial 02

Local setup and first run

Run the first Kix example locally on kind and make one edit.

In this tutorial you will deploy one tiny Kix cluster to kind, port-forward to it, and make one config edit. The point is to feel the loop:

  1. Edit the cluster or package.
  2. Check what Kix can prove before touching the cluster.
  3. Deploy the checked activation.
  4. Verify with kix pf, curl, or kubectl.
  • Read Orientation: what Kix is and why it exists.
  • Install Nix with flakes enabled.
  • Have Docker, Colima, or OrbStack running.
  • Have kind, kubectl, and the kix CLI available. The next section shows one way to get them.

The runnable examples live in the public kix-examples repo:

❱ git clone https://github.com/kix-ops/kix-examples.git
❱ cd kix-examples

If you already have the repo, use that checkout instead. The commands below assume you are at the repo root.

Open the files for the first example:

flake.nix
tutorials/02-hello-world/cluster.nix
tutorials/02-hello-world/README.md
kind-config.yaml

You do not need to understand all of them yet. For now:

  • flake.nix exposes the example clusters by name;
  • tutorials/02-hello-world/cluster.nix defines the first cluster;
  • kind-config.yaml creates a local kind cluster named kix-demo.

If your machine already has kix, kind, and kubectl on PATH, you can use your normal shell. If you need the Kix CLI, install it from the public kixpkgs flake:

❱ nix profile install github:kix-run/kixpkgs#kix

Keep Docker, Colima, or OrbStack running separately. kind needs a Docker-compatible daemon to create the local cluster.

Check that Kix can see the example clusters:

kix-examples/
❱ kix list clusters
 NAME                              
 02-hello-world                    
 06-service-dep                    
 08-namespace-deps                 
 09-typed-options                  
 10-reuse-packages                 
 19-scorecards                     
 how-to-adoption                   
 how-to-adoption-takeover          
 how-to-application                
 how-to-auto-instantiation         
 how-to-composition                
 how-to-fragments                  
 how-to-helm-bridge                
 how-to-multi-env                  
 how-to-package-cilium             
 how-to-package-monitoring         
 how-to-package-monitoring-loki    
 how-to-package-stackgres          
 how-to-package-storage            
 how-to-package-valkey             
 how-to-package-velero             
 how-to-package-victoria-metrics   
 how-to-platform-cloudflare-tunnel 
 how-to-platform-gateway           
 how-to-platform-hostpath          
 how-to-platform-ingress           
 how-to-platform-monitoring        
 how-to-platform-network-policy    
 how-to-platform-secrets           
 how-to-platform-sops              
 how-to-stateful-migration

You should see 02-hello-world in the output. If you are not in the example repo, point Kix at it explicitly:

❱ kix --flake /path/to/kix-examples list clusters

The --flake flag tells Kix which flake to evaluate. Without it, Kix uses the current directory.

Start Docker, then create the kind cluster:

kix-examples/
❱ kind create cluster --config kind-config.yaml

This creates a single-node cluster named kix-demo and sets your current kubectl context to kind-kix-demo.

Check that Kubernetes is reachable:

kix-examples/
❱ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:40711
CoreDNS is running at https://127.0.0.1:40711/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
kix-examples/
❱ kubectl get nodes
NAME                     STATUS     ROLES           AGE   VERSION
kix-docs-control-plane   NotReady   control-plane   4s    v1.35.0

If you already have this kind cluster, kind will say it exists. That is fine; continue with the deploy step.

Deploy the first Kix example:

kix-examples/ live capture
❱ kix deploy 02-hello-world -y
Show outputHide output · 39 lines
Building cluster '02-hello-world'...
Cluster 02-hello-world: 10 manifests
Connecting to cluster...
No previous activation on cluster. First deploy.

  _cluster
    ~ cluster-level resources (4 added)
  kube-system
    + platform-dns (0 resources)
  tutorial-02
    + hello-world 1.27 (3 resources)

  Plan: cluster-level changes, 2 added
  Resources: 4 real content, 0 dep-affected
plan: 10 nodes
  + Namespace/tutorial-02 created
  ✔ Namespace/tutorial-02 ready
  + ConfigMap/hello-world@tutorial-02 created
  ✔ ConfigMap/hello-world@tutorial-02 ready
  + CustomResourceDefinition/activations.kix.run created
  + Deployment/hello-world@tutorial-02 created
  ✔ CustomResourceDefinition/activations.kix.run ready
  ~ Namespace/kube-system configured
  ✔ Namespace/kube-system ready
  + CustomResourceDefinition/packageinstances.kix.run created
   1.140286895s  WARN kix::cluster::client: apiserver request failed, retrying method=GET path="/apis/kix.run/v1alpha1/activations" attempt=1 max_attempts=7 delay_ms=1000 reason="429 Too Many Requests"
  ✔ CustomResourceDefinition/packageinstances.kix.run ready
  + PackageInstance/platform-dns@kube-system created
  ✔ PackageInstance/platform-dns@kube-system ready
  ✔ Deployment/hello-world@tutorial-02 ready
  + Service/hello-world@tutorial-02 created
  ✔ Service/hello-world@tutorial-02 ready
  + PackageInstance/hello-world@tutorial-02 created
  ✔ PackageInstance/hello-world@tutorial-02 ready
  ~ Activation/02-hello-world-sgnxg5lby2jc configured
  ✔ Activation/02-hello-world-sgnxg5lby2jc ready
  • activation '02-hello-world-sgnxg5lby2jc' → Active

Deploy complete: 8 created, 2 configured, 0 unchanged, 0 failed

The -y flag skips the confirmation prompt. On the first deploy, Kix builds the cluster, sees there is no previous activation, and applies the resources wave-by-wave — the order you see comes from the deploy graph.

List the packages installed in this Kix cluster:

kix-examples/
❱ kix list packages --cluster 02-hello-world
 NAME              VERSION  OWNER     STATUS                  
 hello-world       1.30.5   app-team  installed [tutorial-02] 
 platform-dns      -        -         import [kube-system]    
 platform-storage  -        -         import [kube-system]

You should see an installed hello-world instance. The import rows are platform pieces (DNS, storage) Kix pulls in automatically — later tutorials explain them. You can also ask Kubernetes directly:

❱ kubectl get pods -n tutorial-02
NAME                          READY   STATUS    RESTARTS   AGE
hello-world-5dfdf5bdd-f98hz   1/1     Running   0          24s
❱ kubectl get svc -n tutorial-02
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
hello-world   ClusterIP   10.96.166.102   <none>        80/TCP    1s

The first example installs an echo-server package instance named hello-world. Use kix pf to port-forward to its primary workload:

kix-examples/
❱ kix pf 02-hello-world hello-world 8080:80

Leave that command running. In another terminal, call the service:

❱ curl http://localhost:8080/
Hello from Kix!

Stop the port-forward with Ctrl-C.

kix pf resolves the package instance to the workload Kix built, then delegates to kubectl port-forward. The package name can also be written as namespace/name when a cluster has duplicate instance names, but this first cluster only has one.

Open tutorials/02-hello-world/cluster.nix and find this instance:

tutorials/02-hello-world/cluster.nix
hello-world = {
# `packages.echo-server` ships in the public kixpkgs
# repository, along with many others.
# It serves a simple fixed message over HTTP.
package = packages.echo-server;
# `config` is whatever the package's options accept.
# echo-server's `message` becomes the body of every response.
config = {
message = "Hello from Kix!";
};
};

View source on GitHub ↗

Change the message:

kix-examples/tutorials/02-hello-world/cluster.nix
config = {
message = "Hello from my laptop!";
};

Deploy again:

kix-examples/ live capture
❱ kix deploy 02-hello-world -y
Show outputHide output · 25 lines
Building cluster '02-hello-world'...
Cluster 02-hello-world: 10 manifests
Connecting to cluster...
Active activation: 02-hello-world-sgnxg5lby2jc (sgnxg5lb...)

  tutorial-02
    ~ hello-world 1.27 (1 changed, 3 dep-affected)

  Plan: 1 updated, 1 unchanged
  Resources: 1 real content, 3 dep-affected
plan: 10 nodes
  ~ ConfigMap/hello-world@tutorial-02 configured
  ✔ ConfigMap/hello-world@tutorial-02 ready
  ~ Deployment/hello-world@tutorial-02 configured
  ✔ Deployment/hello-world@tutorial-02 ready
  ~ Service/hello-world@tutorial-02 configured
  ✔ Service/hello-world@tutorial-02 ready
  ~ PackageInstance/hello-world@tutorial-02 configured
  ✔ PackageInstance/hello-world@tutorial-02 ready
  ~ Activation/02-hello-world-5iyml5dk18c8 configured
  ✔ Activation/02-hello-world-5iyml5dk18c8 ready
  • activation '02-hello-world-sgnxg5lby2jc' → Superseded
  • activation '02-hello-world-5iyml5dk18c8' → Active

Deploy complete: 0 created, 5 configured, 5 unchanged, 0 failed

Port-forward and curl again:

kix-examples/
❱ kix pf 02-hello-world hello-world 8080:80

In another terminal:

❱ curl http://localhost:8080/
Hello from my laptop!

The response should show your new message.

That edit is small, but it proves the core loop: Kix evaluated the flake, rebuilt the manifests, applied the changed workload, and left you with a normal Kubernetes Service and Pod.

You have now used:

  • kix list clusters to discover cluster names;
  • kix deploy <cluster> -y to apply one cluster;
  • kix list packages --cluster <cluster> to inspect installed packages;
  • kix pf <cluster> <package> <local>:<remote> to reach a workload.

The next tutorial steps back from the running cluster and looks at the repo shape that made those commands work.