Skip to content

02. Local setup and first run

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:

Run in 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-python             
 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:

Run in 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:

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

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Run in 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:

Run in kix-examples/
❱ kix deploy 02-hello-world -y Show output
Building cluster '02-hello-world'...
Cluster 02-hello-world: 10 manifests
Connecting to cluster...
No previous activation on cluster — first deploy.
Reading live cluster state...

  _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/kube-system configured
  ✔ Namespace/kube-system ready
  + Namespace/tutorial-02 created
  ✔ Namespace/tutorial-02 ready
  + CustomResourceDefinition/packageinstances.kix.run created
  + CustomResourceDefinition/activations.kix.run created
  + ConfigMap/hello-world@tutorial-02 created
  ✔ ConfigMap/hello-world@tutorial-02 ready
  + Deployment/hello-world@tutorial-02 created
  ✔ CustomResourceDefinition/packageinstances.kix.run ready
  ✔ CustomResourceDefinition/activations.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-rh3pdaspsdxk created
  ✔ Activation/02-hello-world-rh3pdaspsdxk ready
  • activation '02-hello-world-rh3pdaspsdxk' → Active

Deploy complete: 9 created, 1 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:

Run in kix-examples/
❱ kix list packages --cluster 02-hello-world
 NAME              VERSION  OWNER     STATUS                  
 hello-world       1.27     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-6f4588d4c5-pwt94   1/1     Running   0          21s
❱ kubectl get svc -n tutorial-02
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
hello-world   ClusterIP   10.96.142.137   <none>        80/TCP    2s

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

Run in 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 (L48–L59)
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:

Run in kix-examples/
❱ kix deploy 02-hello-world -y Show output
Building cluster '02-hello-world'...
Cluster 02-hello-world: 10 manifests
Connecting to cluster...
Active activation: 02-hello-world-rh3pdaspsdxk (rh3pdasp...)
Reading live cluster state...

  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-ai8hpfnhamfs created
  ✔ Activation/02-hello-world-ai8hpfnhamfs ready
  • activation '02-hello-world-rh3pdaspsdxk' → Superseded
  • activation '02-hello-world-ai8hpfnhamfs' → Active

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

Port-forward and curl again:

Run in 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.