Skip to content

04. First cluster from scratch

In the previous tutorial you read the files that make an example cluster work. Now you will create one small cluster yourself.

You will add a new cluster to your kix-examples checkout, deploy one echo-server instance to kind, and reach it through kix pf.

❱ git clone https://github.com/kix-ops/kix-examples.git
❱ cd kix-examples
  • Keep the kix-demo kind cluster running, or recreate it with:
Run in kix-examples/
❱ kind create cluster --config kind-config.yaml

Create a new tutorial directory:

Run in kix-examples/
❱ mkdir -p tutorial-04

Create tutorial-04/cluster.nix:

tutorial-04/cluster.nix
{ kix, packages }:
kix.buildCluster {
name = "04-from-scratch";
modules = [
kix.flavors.kind
{
namespaces = {
tutorial-04 = { };
};
instances.tutorial-04 = {
hello-world = {
package = packages.echo-server;
config = {
message = "Hello from my first Kix cluster!";
};
};
};
}
];
}

Most of this should look familiar from the previous tutorial:

  • { kix, packages }: says this file receives the Kix helpers and package catalog.
  • kix.buildCluster builds one named cluster.
  • kix.flavors.kind says the target is a local kind cluster.
  • namespaces.tutorial-04 = { }; tells Kix to manage one namespace.
  • instances.tutorial-04.hello-world installs one package instance in that namespace.

The new thing is that you wrote the file yourself.

Open flake.nix and add one line inside the clusters = { ... }; block:

flake.nix
clusters = {
# existing example entries...
"04-from-scratch" = ./tutorial-04/cluster.nix;
};

The left side, "04-from-scratch", is the cluster name you will pass to the CLI. The right side points at the file you just created.

Because kix-examples is a Git flake, Nix only sees files known to Git. Stage the new file and the flake edit before running Kix:

Run in kix-examples/
❱ git add flake.nix tutorial-04/cluster.nix

You do not need to commit. Staging is enough for Nix to include the new file in the flake source.

List the clusters:

Run in kix-examples/
❱ kix list clusters
 NAME                              
 02-hello-world                    
 04-from-scratch                   
 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 04-from-scratch in the list.

If you do not, check three things:

  • you added the line inside the clusters = { ... }; block;
  • the file path is ./tutorial-04/cluster.nix;
  • you staged both flake.nix and tutorial-04/cluster.nix.

Deploy the cluster:

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

  _cluster
    ~ cluster-level resources (1 added, 1 removed)
  tutorial-02
    - hello-world 1.27 (3 resources)
  tutorial-04
    + hello-world 1.27 (3 resources)

  Plan: cluster-level changes, 1 added, 1 removed, 1 unchanged
  Resources: 2 real content, 0 dep-affected
  5 orphaned (kept; pass --prune to delete)
    - Service/hello-world@tutorial-02
    - Namespace/tutorial-02
    - ConfigMap/hello-world@tutorial-02
    - Deployment/hello-world@tutorial-02
    - PackageInstance/hello-world@tutorial-02
plan: 10 nodes
  + Namespace/tutorial-04 created
  ✔ Namespace/tutorial-04 ready
  + ConfigMap/hello-world@tutorial-04 created
  ✔ ConfigMap/hello-world@tutorial-04 ready
  + Deployment/hello-world@tutorial-04 created
  ✔ Deployment/hello-world@tutorial-04 ready
  + Service/hello-world@tutorial-04 created
  ✔ Service/hello-world@tutorial-04 ready
  + PackageInstance/hello-world@tutorial-04 created
  ✔ PackageInstance/hello-world@tutorial-04 ready
  + Activation/04-from-scratch-m7cpzxzyff67 created
  ✔ Activation/04-from-scratch-m7cpzxzyff67 ready

prune: 5 orphaned resources kept (warn-only; pass --prune to delete)
  - PackageInstance/hello-world@tutorial-02
  - Service/hello-world@tutorial-02
  - Deployment/hello-world@tutorial-02
  - ConfigMap/hello-world@tutorial-02
  - Namespace/tutorial-02
  • activation '04-from-scratch-m7cpzxzyff67' → Active

Deploy complete: 6 created, 0 configured, 4 unchanged, 0 failed

Ask Kubernetes what changed:

❱ kubectl get pods -n tutorial-04
NAME                           READY   STATUS    RESTARTS   AGE
hello-world-6f4588d4c5-458tf   1/1     Running   0          7s
❱ kubectl get svc -n tutorial-04
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
hello-world   ClusterIP   10.96.209.101   <none>        80/TCP    5s

You should see one Pod and one Service for the hello-world instance.

Port-forward to the package instance:

Run in kix-examples/
❱ kix pf 04-from-scratch hello-world 8080:80

Leave that running. In another terminal:

❱ curl http://localhost:8080/
Hello from my first Kix cluster!

Stop the port-forward with Ctrl-C.

Change the message in tutorial-04/cluster.nix:

tutorial-04/cluster.nix
config = {
message = "Kix rebuilt this from my cluster definition.";
};

Deploy again:

Run in kix-examples/
❱ kix deploy 04-from-scratch -y Show output
Building cluster '04-from-scratch'...
Cluster 04-from-scratch: 10 manifests
Connecting to cluster...
Active activation: 04-from-scratch-m7cpzxzyff67 (m7cpzxzy...)
Reading live cluster state...

  _cluster
    ~ cluster-level resources (1 removed)
  tutorial-02
    - hello-world 1.27 (3 resources)
  tutorial-04
    ~ hello-world 1.27 (1 changed, 3 dep-affected)

  Plan: cluster-level changes, 1 updated, 1 removed, 1 unchanged
  Resources: 2 real content, 3 dep-affected
  5 orphaned (kept; pass --prune to delete)
    - Service/hello-world@tutorial-02
    - ConfigMap/hello-world@tutorial-02
    - Deployment/hello-world@tutorial-02
    - Namespace/tutorial-02
    - PackageInstance/hello-world@tutorial-02
plan: 10 nodes
  ~ ConfigMap/hello-world@tutorial-04 configured
  ✔ ConfigMap/hello-world@tutorial-04 ready
  ~ Deployment/hello-world@tutorial-04 configured
  ✔ Deployment/hello-world@tutorial-04 ready
  ~ Service/hello-world@tutorial-04 configured
  ✔ Service/hello-world@tutorial-04 ready
  ~ PackageInstance/hello-world@tutorial-04 configured
  ✔ PackageInstance/hello-world@tutorial-04 ready
  + Activation/04-from-scratch-qibygg9r95ww created
  ✔ Activation/04-from-scratch-qibygg9r95ww ready

prune: 5 orphaned resources kept (warn-only; pass --prune to delete)
  - PackageInstance/hello-world@tutorial-02
  - Service/hello-world@tutorial-02
  - Deployment/hello-world@tutorial-02
  - ConfigMap/hello-world@tutorial-02
  - Namespace/tutorial-02
  • activation '04-from-scratch-m7cpzxzyff67' → Superseded
  • activation '04-from-scratch-qibygg9r95ww' → Active

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

Port-forward and curl again. The response should use your new message.

You created the smallest useful Kix cluster from scratch:

  • a cluster file that calls kix.buildCluster;
  • a kind flavor;
  • one managed namespace;
  • one package instance;
  • one flake entry that gives the cluster a CLI name.

The next tutorial slows down on the deploy loop. You will check, build, diff, and inspect this cluster before applying changes.