Skip to content
Install the CLI

Reference Clusters and instances

Flavors and capabilities

Every Kubernetes provider comes with a different set of built-in features: a managed cloud cluster like EKS or GKE handles control-plane components and load balancers for you, while a local kind or bare-metal cluster leaves more for you to install. A Kix flavor (kix.flavors.<name>) is a ready-made preset you include at the top of kix.buildCluster so Kix knows what your cluster already provides and what capabilities your nodes support.

Include a flavor as the first module in kix.buildCluster:

{ kix, packages }:
kix.buildCluster {
specialArgs = { inherit kix packages; };
modules = [
kix.flavors.eks
./platform.nix
./services.nix
];
}

Every setting in a flavor uses lib.mkDefault, so your cluster modules can override any capability, role binding, or network setting when your cluster differs from the stock distribution.

Kix ships 12 built-in flavor modules under kix.flavors:

Flavorcluster.providerTarget environmentNotable flavor defaults
kix.flavors.aks"aks"Azure Kubernetes ServiceManaged control plane, Azure CNI, Azure Load Balancer, managed-csi StorageClass, and discardUnpackedLayers = false.
kix.flavors.eks"eks"Amazon EKSManaged control plane, aws-vpc-cni, AWS cloud load balancer, storageClasses and metricsApi bound "absent", and discardUnpackedLayers = true.
kix.flavors.gke"gke"Google Kubernetes Engine (Standard)Managed control plane, GKE Dataplane V2, GCP Network Load Balancer, standard-rwo StorageClass, and /etc/containerd/hosts.d registry config path.
kix.flavors.gke-autopilot"gke-autopilot"Google Kubernetes Engine (Autopilot)Sets daemonSets = false, privilegedContainers = false, nodeAutoscaling = true, podSecurityEnforcement = "baseline", and places platform imports in gke-managed (since kube-system is restricted).
kix.flavors.k3s"k3s"k3sSets podCidr = "10.42.0.0/16", k3s containerd paths under /run/k3s and /var/lib/rancher/k3s, Flannel CNI, ServiceLB, local-path StorageClass, and metrics-server.
kix.flavors.kind"kind"kind (Kubernetes in Docker)Sets multiNode = false, availabilityZones = [ ], podCidr = "10.244.0.0/16", kindnet CNI, standard StorageClass, and platform network policy intents for CoreDNS and local-path-provisioner.
kix.flavors.kubeadm"kubeadm"Upstream kubeadmMarks cni, storageClasses, loadBalancer, and metricsApi as "absent" so Kix packages can provide them; imports platform-dns.
kix.flavors.openshift"openshift"Red Hat OpenShiftSets privilegedContainers = false, podSecurityEnforcement = "restricted", ovn-kubernetes CNI, openshift-monitoring for metricsApi, and CoreDNS at dns-default.openshift-dns.svc.
kix.flavors.ovh"ovh"OVHcloud Managed Kubernetes (MKS)Sets multiNode = true, managedControlPlane = true, Canal CNI, csi-cinder-high-speed StorageClass, and paid OVH load balancer ("shadowable").
kix.flavors.rke2"rke2"RKE2Exposes cluster.flavor.rke2.cni (default [ "canal" ]), sets podCidr = "10.42.0.0/16", RKE2 containerd paths, rke2-snapshot-controller import, and kube-system platform network policy intents.
kix.flavors.scaleway"scaleway"Scaleway KapsuleSets multiNode = true, managedControlPlane = true, Kapsule-managed Cilium CNI ("exclusive"), sbs-default StorageClass, and discardUnpackedLayers = false.
kix.flavors.talos"talos"Talos LinuxSets Flannel CNI ("exclusive"), storageClasses, loadBalancer, and metricsApi as "absent", and /etc/cri/conf.d/hosts for containerd registry mirrors.

The rke2 flavor adds the option cluster.flavor.rke2.cni (listOf ("multus" | "canal" | "calico" | "cilium" | "flannel" | "none"), default [ "canal" ]). Set it to match the cni list in your RKE2 server configuration:

  • "canal", "calico", or "flannel": binds cluster.roles.cni as "exclusive".
  • "cilium": binds cluster.roles.cni as "shadowable" and declares instances.kube-system.platform-cilium with out.helmChartConfig = { name = "rke2-cilium"; namespace = "kube-system"; }, allowing a packages.cilium instance to configure RKE2’s bundled Cilium chart via a HelmChartConfig resource.
  • [ "none" ]: binds cluster.roles.cni as "absent" so a managed CNI can be installed from scratch.
  • "multus" may precede one primary CNI (for example, [ "multus" "cilium" ]).

Each flavor sets defaults under cluster.capabilities and cluster.containerd. Cells marked null are left unset by the flavor so you can declare them per cluster (for example, multiNode and availabilityZones, which depend on your node pools).

FlavormultiNodeavailabilityZonesmanagedControlPlanenodeAutoscalingprivilegedContainersdaemonSetspodSecurityEnforcementcontainerd.discardUnpackedLayers
aksnullnulltruefalsetruetruenullfalse
eksnullnulltruefalsetruetruenulltrue
gkenullnulltruefalsetruetruenulltrue
gke-autopilotnullnulltruetruefalsefalse"baseline"null
k3snullnullfalsefalsetruetruenullfalse
kindfalse[ ]falsefalsetruetruenulltrue
kubeadmnullnullfalsefalsetruetruenullnull
openshiftnullnullfalsefalsefalsetrue"restricted"null
ovhtruenulltruefalsetruetruenullnull
rke2nullnullfalsefalsetruetruenullfalse
scalewaytruenulltruefalsetruetruenullfalse
talosnullnullfalsefalsetruetruenulltrue

gke also sets cluster.capabilities.gpu = false by default (override to true when attaching a GPU node pool).

Each flavor configures cluster.roles.<role>.binding. No flavor binds network-policy-enforcer; declare an enforcer package such as packages.cilium when enabling networkPolicy.enable.

FlavorcnidnsstorageClassesloadBalancermetricsApisnapshotControllerregistryMirror
aksexclusive (azure-cni)shadowableshadowableshadowable (azure-lb)exclusivenullnull
eksexclusive (aws-vpc-cni)shadowableabsentshadowable (aws-cloud-provider)absentnullnull
gkeexclusive (gke-native)shadowableshadowableshadowable (gcp-network-lb)exclusivenullnull
gke-autopilotexclusive (gke-native)shadowableshadowableexclusive (gcp-network-lb)exclusivenullnull
k3sexclusive (flannel)shadowableshadowableexclusive (servicelb)exclusivenullnull
kindexclusive (kindnet)shadowableshadowableabsentabsentabsentabsent
kubeadmabsentshadowableabsentabsentabsentnullnull
openshiftexclusive (ovn-kubernetes)shadowableabsentabsentexclusive (openshift-monitoring)nullnull
ovhexclusive (canal)shadowableshadowableshadowable (ovh-loadbalancer)nullnullnull
rke2via rke2.cni (default exclusive canal)shadowableabsentabsentexclusive (rke2-metrics-server)exclusive (rke2-snapshot-controller)null
scalewayexclusive (cilium)shadowableshadowableshadowable (scaleway-lb)nullnullnull
talosexclusive (flannel)shadowableabsentabsentabsentnullnull

Flavors declare kix.mkImport instances to represent services and CRDs that the platform installs out of band. Because dependency resolution prefers managed instances over imports, deploying a managed package that claims the same role (such as packages.coredns or packages.storage-classes) shadows a "shadowable" platform import automatically.

Import instanceClaimed roleDeclared by flavorsoptionalPurpose
kube-system/platform-dns (gke-managed/platform-dns on gke-autopilot)dnsAll flavorsfalse on kind; true on all other flavorsRepresents the cluster’s built-in DNS Deployment (coredns or kube-dns). When active, kix deploy verifies the Deployment exists before applying dependent workloads.
kube-system/platform-storage (gke-managed/platform-storage on gke-autopilot)storageClassesaks, gke, gke-autopilot, k3s, kind, ovh, scalewayfalseRepresents the default StorageClass created by the provider. Always active so optional storageClasses dependencies on StatefulSets and PVCs resolve to it and cross-resource check 20 sees the class.
kube-system/platform-ciliumcnirke2 (when cluster.flavor.rke2.cni includes "cilium")falseExposes out.helmChartConfig = { name = "rke2-cilium"; namespace = "kube-system"; } so a managed cilium instance writes a HelmChartConfig instead of deploying a second Cilium chart.
kube-system/platform-snapshot-controllersnapshotControllerrke2falseExposes out.crds handles (VolumeSnapshotClass, VolumeSnapshotContent, VolumeSnapshot) for RKE2’s bundled snapshot controller.

The table below lists the exact DNS Service FQDNs and default StorageClass names exposed by each flavor’s platform-dns and platform-storage imports:

FlavorImport namespaceplatform-dns Deployment nameplatform-dns.out.fqdnplatform-storage (out.storageClassName)
akskube-systemcorednskube-dns.kube-system.svc.<clusterDomain>"managed-csi"
ekskube-systemcorednskube-dns.kube-system.svc.<clusterDomain>none (storageClasses is "absent"; install the EBS CSI driver or declare a storage-classes instance)
gkekube-systemkube-dnskube-dns.kube-system.svc.<clusterDomain>"standard-rwo"
gke-autopilotgke-managedkube-dnskube-dns.kube-system.svc.<clusterDomain>"premium-rwo"
k3skube-systemcorednskube-dns.kube-system.svc.<clusterDomain>"local-path"
kindkube-systemcorednskube-dns.kube-system.svc.<clusterDomain>"standard"
kubeadmkube-systemcorednskube-dns.kube-system.svc.<clusterDomain>none (storageClasses is "absent")
openshiftkube-systemcorednsdns-default.openshift-dns.svc.<clusterDomain>none (storageClasses is "absent" by default)
ovhkube-systemcorednskube-dns.kube-system.svc.<clusterDomain>"csi-cinder-high-speed"
rke2kube-systemcorednskube-dns.kube-system.svc.<clusterDomain>none (storageClasses is "absent")
scalewaykube-systemcorednskube-dns.kube-system.svc.<clusterDomain>"sbs-default"
taloskube-systemcorednskube-dns.kube-system.svc.<clusterDomain>none (storageClasses is "absent")

When a flavor imports a default StorageClass (cluster.roles.storageClasses.binding = "shadowable"), kix.storage.mkClasses (used by packages.storage-classes) refuses to annotate a managed StorageClass with storageclass.kubernetes.io/is-default-class: "true" because the cluster’s existing default class is not removed by Kix and Kubernetes would see two defaults. (When multiple rendered StorageClass manifests in the same build are marked default, cross-resource validation check 23 also fails the build.)