Skip to content

Declarative Configuration

KSail uses declarative YAML configuration files for reproducible cluster setup. This page describes ksail.yaml — the project-level configuration file that defines your cluster’s desired state.

Each KSail project includes a ksail.yaml file describing cluster distribution, networking, components, and workload configuration. Run ksail project init to generate it — commit to version control to share with your team.

KSail supports environment variable expansion in all string configuration values using the ${VAR_NAME} syntax for secure credentials, environment-specific paths, and dynamic values.

Basic syntax: ${VARIABLE_NAME} — Reference an environment variable. If not set, expands to an empty string and logs a warning.

Default value syntax: ${VARIABLE_NAME:-default} — Use a default value if the variable is not set. No warning is logged when using defaults.

spec:
editor: "${EDITOR:-vim}"
cluster:
connection:
kubeconfig: "${HOME}/.kube/config"
context: "${KUBE_CONTEXT:-kind-kind}"
distributionConfig: "${CONFIG_DIR:-configs}/kind.yaml"
localRegistry:
registry: "${REGISTRY:-localhost:5000}"
vanilla:
mirrorsDir: "${MIRRORS_DIR:-mirrors}"
talos:
config: "${TALOS_CONFIG_PATH:-~/.talos/config}"
provider:
hetzner:
sshKeyName: "${HCLOUD_SSH_KEY}"
workload:
sourceDirectory: "${WORKLOAD_DIR:-k8s}"
chat:
model: "${CHAT_MODEL:-gpt-4o}"
Syntax Variable Set Variable Not Set
${VAR} Uses value Empty string + warning
${VAR:-default} Uses value Uses default (no warning)
${VAR:-} Uses value Empty string (no warning)

Environment variables are expanded in all string fields of ksail.yaml, distribution configs (kind.yaml, k3d.yaml), and Talos patch files (talos/cluster/, talos/control-planes/, talos/workers/):

# kind.yaml - Environment variables are expanded before parsing
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."${REGISTRY:-localhost:5000}"]
endpoint = ["http://${REGISTRY:-localhost:5000}"]
# talos/cluster/registry.yaml - Environment variables are expanded
machine:
registries:
mirrors:
docker.io:
endpoints:
- http://${REGISTRY:-localhost:5000}

Because patch files are expanded too, you can inject registry credentials into the Talos machine config as config-as-code — keeping the secret in the environment, never committed. When localRegistry.credentials is configured, Talos maps placeholders for both tokenEnvVar and clusterTokenEnvVar to the effective cluster pull source. Other placeholders still resolve directly. A configured cluster source remains authoritative even when its variable is missing or empty, so a default expression cannot silently substitute a broader credential:

# talos/cluster/registry-auth.yaml - Credentials are injected from the environment at load time
machine:
registries:
config:
registry.example.com:
auth:
username: ${REGISTRY_USER}
# The common placeholder maps to the effective cluster token source.
password: ${REGISTRY_TOKEN:-forbidden-fallback}

Credentials may be embedded directly in the registry spec, where ${VAR_NAME} placeholders are expanded at load time:

spec:
cluster:
localRegistry:
registry: "${REGISTRY_USER}:${REGISTRY_PASS}@${REGISTRY_HOST:-ghcr.io}/myorg/myrepo"

Example: Separate push and pull credentials

Section titled “Example: Separate push and pull credentials”

To give the cluster a least-privilege, pull-only token while the CLI keeps a token that can also push, declare which environment variable each execution path reads. Following the KSail *EnvVar convention, these fields hold the name of an environment variable, never a token value:

spec:
cluster:
localRegistry:
registry: "${REGISTRY_USER}@registry.example.com/myorg/myrepo"
credentials:
tokenEnvVar: REGISTRY_TOKEN
cliTokenEnvVar: REGISTRY_PUBLISH_TOKEN
clusterTokenEnvVar: REGISTRY_PULL_TOKEN

Resolution is deterministic and registry-agnostic — no registry host is special-cased:

  • CLI and publish paths read cliTokenEnvVar, falling back to tokenEnvVar only when the override is not configured.
  • Cluster pull paths — the Flux registry Secret, Argo CD repository credentials, and Talos node authentication — read clusterTokenEnvVar, falling back to tokenEnvVar the same way.
  • A configured override stays authoritative even when its environment variable is missing or empty; KSail never silently falls back based on process-environment state.
  • When no field is set, the password embedded in registry is used.

When clusterTokenEnvVar resolves to a different variable than the push path, the credential KSail persists into the cluster is marked pull-only, so it is never reused to publish artifacts.

Terminal window
export REGISTRY_USER="registry-user"
export REGISTRY_PUBLISH_TOKEN="write-capable-token"
export REGISTRY_PULL_TOKEN="read-only-token"
ksail cluster create
spec:
cluster:
connection:
context: "${CLUSTER_NAME:-kind-kind}"
distributionConfig: "${ENV:-dev}/kind.yaml"
workload:
sourceDirectory: "${ENV:-dev}/k8s"
Terminal window
# Development (using defaults)
ksail cluster create
# Production (override with environment variables)
export ENV="prod"
export CLUSTER_NAME="prod-cluster"
ksail cluster create
# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.json
apiVersion: ksail.io/v1alpha1
kind: Cluster
spec:
cluster:
distribution: Vanilla
distributionConfig: kind.yaml

This minimal configuration creates a Vanilla cluster (implemented with Kind) using defaults for all other settings.

# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.json
apiVersion: ksail.io/v1alpha1
kind: Cluster
spec:
editor: code --wait
cluster:
distribution: Vanilla
distributionConfig: kind.yaml
connection:
kubeconfig: ~/.kube/config
context: kind-kind
timeout: 5m
cni: Cilium
csi: Default
metricsServer: Enabled
certManager: Enabled
policyEngine: Kyverno
localRegistry:
registry: localhost:5050
gitOpsEngine: Flux
workload:
sourceDirectory: k8s
validateOnPush: true
Field Type Required Description
apiVersion string Yes Must be ksail.io/v1alpha1
kind string Yes Must be Cluster
spec object Yes Cluster and workload specification (see below)

The spec field is a Spec object that defines editor, cluster, and workload configuration.

Field Type Default Description
editor string Editor command for interactive workflows (e.g. code –wait). CLI-only; ignored by the operator.
cluster ClusterSpec Cluster configures the Kubernetes cluster KSail manages: distribution, provider, components, and connection settings.
provider ProviderSpec Provider holds infrastructure-provider-specific options (Hetzner, Omni, AWS, GCP, Azure, and the Kubernetes provider for nested clusters).
workload WorkloadSpec Workload configures workload management: the manifest source directory, OCI push and validation settings, and GitOps bootstrap options.
chat ChatSpec Chat configures the KSail AI chat assistant. CLI-only; ignored by the operator (the Cluster CRD shares this type but never reads it).

Editor command for interactive workflows (e.g., code --wait, vim). Falls back to SOPS_EDITOR, KUBE_EDITOR, EDITOR, VISUAL, or system defaults.

Field Type Default Description
distributionConfig string Path to the distribution’s own configuration file or directory (e.g. kind.yaml, k3d.yaml, talos/, vcluster.yaml, kwok/, eks.yaml, gke.yaml, or aks.yaml). CLI-only; ignored by the operator.
connection Connection Connection defines how KSail connects to the cluster: the kubeconfig path, context name, and operation timeout. CLI-only (local kubeconfig path/context); ignored by the operator.
distribution enum Distribution selects the Kubernetes distribution to provision: Vanilla (Kind), K3s (K3d), Talos, VCluster, KWOK (simulated), EKS (AWS), GKE (Google Cloud), or AKS (Azure).
provider enum Provider selects the infrastructure that runs the cluster nodes: Docker, Hetzner, Omni, AWS, GCP, Azure, or Kubernetes (nested clusters inside an existing cluster). Each distribution supports a subset of providers; when empty, KSail uses the distribution’s default provider.
cni enum CNI selects the Container Network Interface plugin. Default keeps the distribution’s built-in CNI; Cilium or Calico install that CNI instead.
csi enum CSI controls Container Storage Interface support. Default keeps the distribution’s behavior; Enabled installs a CSI driver (local-path-provisioner, or Hetzner CSI on Hetzner); Disabled installs none.
cdi enum CDI controls Container Device Interface support in the container runtime (Default, Enabled, or Disabled).
metricsServer enum MetricsServer controls metrics-server installation. Default keeps the distribution’s behavior; Enabled or Disabled override it.
loadBalancer enum LoadBalancer controls load-balancer support. Default keeps the distribution and provider behavior; Enabled or Disabled override it.
certManager enum CertManager controls whether cert-manager is installed (Enabled or Disabled).
imageVerification enum Container-image signature verification scaffolding for all distributions: Talos scaffolds an ImageVerificationConfig document (1.13+); Vanilla/Kind injects a containerd verifier plugin patch; K3s/K3d scaffolds a containerd config template and mounts it into node containers. Requires verifier binaries (and typically policy) in the node image bin_dir. Disabled skips it.
policyEngine enum PolicyEngine selects the policy engine to install: None, Kyverno, or Gatekeeper.
localRegistry LocalRegistry LocalRegistry configures the host-local OCI registry (or an external registry for cloud providers) used by GitOps workflows.
gitOpsEngine enum GitOpsEngine selects the GitOps engine KSail bootstraps: None, Flux, or ArgoCD.
sops SOPS SOPS configures automatic creation of the SOPS Age secret used to decrypt encrypted manifests in the cluster.
nodeAutoscaling enum Deprecated. Use autoscaler.node.enabled instead. Do not set both nodeAutoscaling and autoscaler.
autoscaler AutoscalerConfig Pod and node autoscaling configuration (supersedes deprecated nodeAutoscaling)
importImages string Path to tar archive with container images to import after cluster creation but before component installation
controlPlanes int32 1 Number of control-plane nodes to create for the cluster (provider/distribution-agnostic)
workers int32 Number of worker nodes to create for the cluster (provider/distribution-agnostic)
kubernetesVersion string Kubernetes version to deploy. When set: cluster create/update reconcile toward it. When unset: cluster update follows the latest stable version and new clusters use a default compatible with the pinned Talos version.
oidc OIDCSpec OIDC authentication configuration for the API server and kubeconfig
vanilla OptionsVanilla Vanilla holds options specific to the Vanilla (Kind) distribution.
talos OptionsTalos Talos holds options specific to the Talos distribution.

See Distributions for detailed information.

  • Vanilla (default) – Standard upstream Kubernetes via Kind
  • K3s – Lightweight Kubernetes via K3d
  • TalosTalos Linux in Docker containers or Hetzner Cloud servers
  • VCluster – Virtual clusters via vCluster
  • KWOK – Simulated clusters via KWOK (control-plane only, no real workloads)
  • EKS – Amazon Elastic Kubernetes Service via eksctl (requires AWS credentials and the eksctl CLI on PATH)
  • GKE – Google Kubernetes Engine via the native Go SDK (requires Google Cloud Application Default Credentials and a project via GOOGLE_CLOUD_PROJECT)
  • AKS – Azure Kubernetes Service via the native Go SDK (requires Azure credentials and a subscription via AZURE_SUBSCRIPTION_ID)

See Providers for more details.

  • Docker (default) – Run nodes as Docker containers (local development)
  • Hetzner – Run nodes on Hetzner Cloud servers (requires HCLOUD_TOKEN)
  • Omni – Manage Talos cluster nodes through Sidero Omni
  • AWS – Manage EKS clusters on Amazon Web Services (requires standard AWS SDK credentials)
  • GCP – Manage GKE clusters on Google Cloud (requires Application Default Credentials)
  • Azure – Manage AKS clusters on Microsoft Azure (requires DefaultAzureCredential-compatible credentials)

Path to the distribution-specific configuration file or directory. This tells KSail where to find settings like node counts, port mappings, and distribution-specific features.

Default values by distribution:

  • Vanillakind.yaml
  • K3sk3d.yaml
  • Talostalos/ (directory)
  • VClustervcluster.yaml
  • KWOKkwok/ (directory)
  • EKSeks.yaml
  • GKEgke.yaml (optional – the GKE API owns the cluster shape)
  • AKSaks.yaml (optional – the AKS API owns the cluster shape)

See Distribution Configuration below for details on each format.

Field Type Default Description
kubeconfig string ~/.kube/config Path to kubeconfig file
context string (derived) Kubeconfig context name
timeout duration Timeout for cluster operations

Context defaults by distribution:

  • Vanillakind-kind
  • K3sk3d-k3d-default
  • Talos (Docker/Hetzner) → admin@talos-default
  • Talos (Omni) → the context name generated by Omni (e.g., devantler-prod)
  • VClustervcluster-docker_vcluster-default
  • KWOKkwok-kwok-default

When using Talos with Omni, Omni generates the context name; set spec.cluster.connection.context to that generated name.

Timeout format: Go duration string (e.g., 30s, 5m, 1h)

See CNI for more details.

  • Default (default) – Uses the distribution’s built-in CNI (kindnetd for Vanilla, flannel for K3s)
  • Cilium – Installs Cilium for advanced networking and observability
  • Calico – Installs Calico for network policies

See CSI for more details.

  • Default (default) – Uses the distribution × provider’s default behavior:
    • K3s: includes local-path-provisioner
    • Vanilla/Talos × Docker: no CSI
    • Talos × Hetzner: includes Hetzner CSI driver
  • Enabled – Explicitly installs CSI driver (local-path-provisioner for local clusters, Hetzner CSI for Talos × Hetzner)
  • Disabled – Disables CSI installation (for K3s, this disables the default local-storage)

Whether to install metrics-server for resource metrics.

  • Default (default) – Uses distribution’s default behavior (K3s includes metrics-server; Vanilla and Talos do not)
  • Enabled – Install metrics-server
  • Disabled – Skip installation

When metrics-server is enabled on Vanilla or Talos, KSail automatically:

  1. Configures kubelet certificate rotation (serverTLSBootstrap: true)
  2. Installs kubelet-csr-approver to approve certificate requests
  3. Deploys metrics-server with secure TLS communication

Whether to install cert-manager for TLS certificate management.

  • Enabled – Install cert-manager
  • Disabled (default) – Skip installation

Policy engine to install for enforcing security, compliance, and best practices. See Policy Engines for details.

Registry configuration for GitOps workflows. Supports local Docker registries or external registries with authentication.

Format: [user:pass@]host[:port][/path]

Examples:

  • localhost:5050 – Local Docker registry
  • ghcr.io/myorg/myrepo – GitHub Container Registry
  • ${USER}:${PASS}@ghcr.io:443/myorg – With credentials from environment variables

GitOps engine for continuous deployment. See GitOps. When set to Flux or ArgoCD, KSail scaffolds a GitOps CR into your source directory.

  • None (default) – No GitOps engine
  • Flux – Install Flux CD and scaffold FluxInstance CR
  • ArgoCD – Install Argo CD and scaffold Application CR

Advanced configuration options are direct fields under spec.cluster. See Schema Support for the complete structure.

Talos options (spec.cluster.talos):

  • controlPlanes – Number of control-plane nodes (default: 1)
  • workers – Number of worker nodes (default: 0)
  • config – Path to talosconfig file (default: ~/.talos/config)
  • version – Pin the Talos OS version (e.g. v1.12.4); caps upgrades and selects the node image (default: built-in)
  • iso – Cloud provider ISO/image ID for Talos Linux (default: 125127 for Talos 1.12.4 x86; for ARM, look up the matching ISO ID under Images → ISOs in the Hetzner Cloud Console)

The Kubernetes version is set at the top level (spec.cluster.kubernetesVersion), not under talos:

  • kubernetesVersion – Pin the Kubernetes version (e.g. v1.32.0). When unset, cluster update keeps the version already running (no unrequested upgrade) and new clusters default to one compatible with the pinned talos.version

Provider options (spec.provider): infrastructure provider options (Hetzner, Omni, AWS, GCP, Kubernetes) are documented in the generated spec.provider (ProviderSpec) sections below.

Autoscaler options (spec.cluster.autoscaler): pod and node autoscaling options are documented in the generated spec.cluster.autoscaler (AutoscalerConfig) sections below.

Vanilla options (spec.cluster.vanilla):

  • mirrorsDir – Directory for containerd host mirror configuration

spec.cluster.autoscaler (AutoscalerConfig)

Section titled “spec.cluster.autoscaler (AutoscalerConfig)”

AutoscalerConfig defines configuration for pod and node autoscaling.

Field Type Default Description
pod PodAutoscalerConfig Pod configures pod-level autoscaling (horizontal and vertical).
node NodeAutoscalerConfig Node configures node-level autoscaling via the Cluster Autoscaler.

spec.cluster.autoscaler.pod (PodAutoscalerConfig)

Section titled “spec.cluster.autoscaler.pod (PodAutoscalerConfig)”

PodAutoscalerConfig defines configuration for pod-level autoscaling.

Field Type Default Description
horizontal enum Horizontal controls Horizontal Pod Autoscaler (HPA) support.
vertical enum Vertical controls Vertical Pod Autoscaler (VPA) support.

spec.cluster.autoscaler.node (NodeAutoscalerConfig)

Section titled “spec.cluster.autoscaler.node (NodeAutoscalerConfig)”

NodeAutoscalerConfig defines configuration for node-level autoscaling. When Enabled, the Cluster Autoscaler manages worker node counts dynamically. KSail-specified node counts serve as a baseline; the autoscaler adds and removes workers based on workload demand. Node-count changes via ksail cluster update are still applied to the Talos machine config and will take effect normally.

Field Type Default Description
enabled enum Whether the Cluster Autoscaler is installed to manage worker node counts dynamically (Enabled or Disabled). A YAML boolean is accepted as an alias (true=Enabled, false=Disabled).
pools []NodePool Pools defines the node pools the Cluster Autoscaler may scale (Hetzner only).
maxNodesTotal int32 Maximum total number of nodes in the cluster (control-planes + workers + autoscaler nodes). Passed verbatim to the cluster-autoscaler –max-nodes-total flag — the autoscaler evaluates it against the count of ALL nodes so this is the whole-cluster ceiling and not an autoscaler-only budget. Set to 0 to disable the global cap; growth is then bounded only by the per-pool max values and serverLimit. A positive value should be <= the effective serverLimit (serverLimit: 0 means its default limit of 10).
expander enum | []enum Node expander strategy for the cluster autoscaler. Accepts either a single value (e.g. LeastWaste) or an ordered priority list (e.g. [LeastNodes, LeastWaste]) applied as a chain — the first expander filters node groups and each later one breaks the previous tie (upstream –expander=least-nodes,least-waste).
scaleDownUnneededTime string How long a node should be unneeded before it is eligible for scale down (e.g. 10m)
scaleDownUtilizationThreshold string Node resource-utilization ratio (0.0–1.0, computed over requests) at or below which the Cluster Autoscaler considers a node for scale down (upstream –scale-down-utilization-threshold, default 0.5). Passed verbatim; leave empty to inherit the upstream default. For agent-heavy clusters prefer ignoreDaemonsetsUtilization, which excludes DaemonSet requests from this calculation. Ignored unless the node autoscaler is installed (Talos on Hetzner with enabled: true).
capacityBuffers boolean Enable the Cluster Autoscaler capacity-buffers feature: KSail installs the CapacityBuffer CRD (capacitybuffers.autoscaling.x-k8s.io) and enables the buffer controller and pod-injection flags. CapacityBuffer resources then reserve scale-up headroom as virtual (pod-less) chunks simulated in autoscaler memory — a native replacement for low-priority balloon-pod overprovisioning. Ignored unless the node autoscaler is installed (Talos on Hetzner with enabled: true)
ignoreDaemonsetsUtilization boolean Exclude DaemonSet pods from a node’s resource-utilization calculation when the Cluster Autoscaler decides whether a node is unneeded (upstream –ignore-daemonsets-utilization, off by default). Enable this when DaemonSets are system components (CNI, CSI, observability, security agents) whose per-node overhead should not keep an otherwise-empty node above the scale-down utilization threshold. Ignored unless the node autoscaler is installed (Talos on Hetzner with enabled: true).
skipNodesWithLocalStorage boolean Whether the Cluster Autoscaler refuses to scale down a node running a pod with local storage (emptyDir, hostPath, or a local PersistentVolume). Upstream –skip-nodes-with-local-storage defaults to true. Set false to let nodes whose only local storage is ephemeral scratch (emptyDir) be removed — required for overflow nodes to drain, since emptyDir is pervasive. Ensure durable data lives on real PVCs first. Ignored unless the node autoscaler is installed (Talos on Hetzner with enabled: true).
skipNodesWithSystemPods boolean Whether the Cluster Autoscaler refuses to scale down a node running a non-DaemonSet kube-system pod that has no controlling PodDisruptionBudget. Upstream –skip-nodes-with-system-pods defaults to true. Set false to let overflow nodes hosting movable system Deployments drain — confirm those components tolerate eviction and carry PDBs first. Ignored unless the node autoscaler is installed (Talos on Hetzner with enabled: true).

spec.cluster.autoscaler.node.pools[] (NodePool)

Section titled “spec.cluster.autoscaler.node.pools[] (NodePool)”

NodePool defines a Hetzner node pool managed by the cluster autoscaler.

Field Type Default Description
name string Name is the unique identifier for this node pool (DNS-1123 label).
serverType string ServerType is the Hetzner server type for nodes in this pool (e.g. “cx23”, “cax11”).
location string Location is the Hetzner datacenter location for this pool (e.g. “fsn1”, “nbg1”).
min int32 Min is the minimum number of nodes in this pool.
max int32 Max is the maximum number of nodes in this pool.
labels map[string]string Kubernetes node labels applied to every node in this pool (via Talos machine.nodeLabels and the autoscaler scale-from-zero template).
taints []NodePoolTaint Kubernetes node taints applied to every node in this pool (via Talos machine.nodeTaints and the autoscaler scale-from-zero template).

spec.cluster.autoscaler.node.pools[].taints[] (NodePoolTaint)

Section titled “spec.cluster.autoscaler.node.pools[].taints[] (NodePoolTaint)”

NodePoolTaint defines a Kubernetes node taint applied to every node in an autoscaler node pool.

Field Type Default Description
key string Key is the taint key. Must be a valid Kubernetes label key (an optional DNS-subdomain prefix followed by a name segment).
value string Value is the optional taint value.
effect enum Effect is the scheduling effect: NoSchedule, PreferNoSchedule, or NoExecute.

ProviderSpec defines provider-specific configuration for infrastructure providers. This separates infrastructure provider concerns (Hetzner servers, Omni SaaS) from cluster/distribution concerns in ClusterSpec.

Field Type Default Description
hetzner OptionsHetzner Hetzner holds options for the Hetzner Cloud provider.
omni OptionsOmni Omni holds options for the Sidero Omni provider.
aws OptionsAWS AWS holds options for the AWS provider used by the EKS distribution.
gcp OptionsGCP GCP holds options for the Google Cloud provider used by the GKE distribution.
azure OptionsAzure Azure holds options for the Microsoft Azure provider used by the AKS distribution.
kubernetes OptionsKubernetes Kubernetes holds options for the Kubernetes provider, which runs nested clusters as pods inside an existing host cluster.

OptionsHetzner defines options specific to the Hetzner Cloud provider. These options are used when Provider is set to “Hetzner” for the Talos distribution.

Field Type Default Description
controlPlaneServerType string cx23 ControlPlaneServerType is the Hetzner server type for control-plane nodes. Examples: “cx23” (x86), “cax11” (ARM), “cpx21” (AMD). Defaults to “cx23”.
workerServerType string cx23 WorkerServerType is the Hetzner server type for worker nodes. Examples: “cx23” (x86), “cax11” (ARM), “cpx21” (AMD). Defaults to “cx23”.
location string fsn1 Location is the Hetzner datacenter location. Examples: “fsn1” (Falkenstein), “nbg1” (Nuremberg), “hel1” (Helsinki). Defaults to “fsn1”.
networkName string NetworkName is the name of the private network to create or use. If empty, a network named “<cluster-name>-network” will be created.
networkCidr string 10.0.0.0/16 NetworkCIDR is the CIDR block for the private network. Defaults to “10.0.0.0/16”.
sshKeyName string SSHKeyName is the name of the SSH key to use for server access. The key must already exist in the Hetzner Cloud project. If empty, no SSH key is attached (only Talos API access).
tokenEnvVar string HCLOUD_TOKEN TokenEnvVar is the environment variable containing the Hetzner API token. Defaults to “HCLOUD_TOKEN”.
placementGroupStrategy enum Spread PlacementGroupStrategy controls whether and how placement groups are used. “Spread” (default) distributes servers across different physical hosts for HA. “None” disables placement groups, useful when Hetzner resources are constrained. Note: Spread groups are limited to 10 servers per datacenter.
placementGroup string PlacementGroup is the name of the placement group for server distribution. If empty, a placement group named “<cluster-name>-placement” will be created. Only used when PlacementGroupStrategy is “Spread”.
fallbackLocations []string Alternative datacenter locations to try when server creation in the primary location fails due to resource unavailability. When empty defaults to nbg1 and hel1 (both in the eu-central network zone matching the default fsn1 primary location).
placementGroupFallbackToNone boolean PlacementGroupFallbackToNone allows automatic fallback to no placement group when spread placement constraints cannot be satisfied (e.g., due to datacenter capacity). When true and placement fails, retries server creation without a placement group. Defaults to false to preserve HA guarantees; set to true for best-effort provisioning.
floatingIPEnabled boolean Provision a Hetzner floating IP and render it as the stable Kubernetes/Talos API endpoint (endpoint + certificate SANs + a control-plane Talos VIP block for leader ownership handover; the hcloud API token is embedded in the control-plane machine config). Defaults to false.
floatingIPLocation string Hetzner location the floating IP is homed in (routing latency only). Defaults to the cluster’s location.
ingressFirewall enum Enabled IngressFirewall controls the Talos OS-level ingress firewall configuration. When Enabled (default), KSail generates NetworkDefaultActionConfig and NetworkRuleConfig documents as Talos machine config patches, providing defense-in-depth at the node level independent of the Hetzner Cloud Firewall. See: https://www.talos.dev/latest/talos-guides/network/ingress-firewall/
serverLimit int32 10 Maximum total Hetzner servers allowed for this cluster — the account/project quota. Validation rejects configs whose reachable total (control-planes + workers + pool capacity clamped by autoscaler.node.maxNodesTotal when set) exceeds it. Set to 0 to use the default limit of 10
allowedCidrs []string CIDR blocks allowed to access the Kubernetes API and Talos API on control-plane nodes. When empty defaults to 0.0.0.0/0 and ::/0 (open to all IPv4 and IPv6).
workerPublicIPv4 boolean Assign a public IPv4 to worker nodes. Defaults to true. Set false for IPv4-less workers reached over the private network (requires private-network reachability and NAT egress).
workerPublicIPv6 boolean Assign a public IPv6 to worker nodes. Defaults to true.
controlPlanePublicIPv4 boolean Assign a public IPv4 to control-plane nodes. Defaults to true. Set false for IPv4-less control planes whose endpoint is the private-network IP (cluster reachable only from inside the private network).
controlPlanePublicIPv6 boolean Assign a public IPv6 to control-plane nodes. Defaults to true.
autoscalerNodePoolNames []string AutoscalerNodePoolNames lists the node-group names configured in the Kubernetes Cluster Autoscaler for this cluster. When non-empty, KSail deletes servers labelled with hcloud/node-group=<name> during cluster deletion so that autoscaler-managed nodes are cleaned up alongside KSail-managed nodes.

OptionsOmni defines options specific to the Sidero Omni provider. These options are used when Provider is set to “Omni” for the Talos distribution.

Field Type Default Description
endpoint string Endpoint is the Omni API endpoint URL. Example: “https://<account>.omni.siderolabs.io:443”.
endpointEnvVar string OMNI_ENDPOINT EndpointEnvVar is the environment variable containing the Omni API endpoint URL. When set, the value of this environment variable takes precedence over Endpoint. Defaults to “OMNI_ENDPOINT”.
serviceAccountKeyEnvVar string OMNI_SERVICE_ACCOUNT_KEY ServiceAccountKeyEnvVar is the environment variable containing the base64-encoded Omni service account key. Defaults to “OMNI_SERVICE_ACCOUNT_KEY”.
talosVersion string TalosVersion is the Talos version to use for the cluster in Omni. Accepts values with or without the “v” prefix (e.g., “v1.11.2” or “1.11.2”). Generated templates normalize the value to include the “v” prefix. This determines the Talos Linux version that Omni will deploy to machines.
kubernetesVersion string KubernetesVersion is the Kubernetes version to use for the cluster in Omni. Accepts values with or without the “v” prefix (e.g., “v1.32.0” or “1.32.0”). Generated templates normalize the value to include the “v” prefix. This determines the Kubernetes version that Omni will deploy.
machineClass string MachineClass is the Omni machine class name to use for dynamic node allocation. Machine classes are user-defined in the Omni dashboard and match machines by labels (e.g., CPU, region, role). The specified class must exist in the Omni account before cluster creation. The number of machines allocated is derived from the controlPlanes and workers count in the cluster spec. Mutually exclusive with Machines — set one or the other. When neither MachineClass nor Machines is set, KSail automatically discovers available (unallocated) machines in Omni and uses them for node allocation.
machines []string Machines is a list of Omni machine UUIDs to use for static node allocation. The first N machines are assigned as control planes (where N = controlPlanes count), and the remaining machines are assigned as workers. Mutually exclusive with MachineClass — set one or the other. When neither MachineClass nor Machines is set, KSail automatically discovers available (unallocated) machines in Omni and uses them for node allocation.

OptionsAWS defines options specific to the AWS cloud provider. Credentials are resolved via the standard AWS SDK v2 credential chain; the *EnvVar fields let users point KSail at non-standard environment variable names (mirrors the Hetzner/Omni pattern).

Field Type Default Description
profileEnvVar string AWS_PROFILE ProfileEnvVar is the environment variable containing the AWS shared-config profile name. Defaults to “AWS_PROFILE”.
regionEnvVar string AWS_REGION RegionEnvVar is the environment variable containing the AWS region. When set, it overrides the region declared in eks.yaml. Defaults to “AWS_REGION”.
accessKeyIdEnvVar string AWS_ACCESS_KEY_ID AccessKeyIDEnvVar is the environment variable containing a static AWS access key ID. Defaults to “AWS_ACCESS_KEY_ID”.
secretAccessKeyEnvVar string AWS_SECRET_ACCESS_KEY SecretAccessKeyEnvVar is the environment variable containing a static AWS secret access key. Defaults to “AWS_SECRET_ACCESS_KEY”.
sessionTokenEnvVar string AWS_SESSION_TOKEN SessionTokenEnvVar is the environment variable containing an AWS session token (used with temporary credentials from STS). Defaults to “AWS_SESSION_TOKEN”.

OptionsGCP defines options specific to the Google Cloud provider used by the GKE distribution. Credentials are resolved via Application Default Credentials (GOOGLE_APPLICATION_CREDENTIALS / gcloud ADC); the *EnvVar fields let users point KSail at non-standard environment variable names (mirrors the AWS/Hetzner/Omni pattern).

Field Type Default Description
projectEnvVar string GOOGLE_CLOUD_PROJECT ProjectEnvVar is the environment variable containing the Google Cloud project ID. Defaults to “GOOGLE_CLOUD_PROJECT”.
locationEnvVar string GOOGLE_CLOUD_LOCATION LocationEnvVar is the environment variable containing the GKE location (zone or region). Defaults to “GOOGLE_CLOUD_LOCATION”.

OptionsAzure defines options specific to the Microsoft Azure provider used by the AKS distribution. Credentials are resolved via the Azure SDK’s DefaultAzureCredential chain (environment, managed identity, Azure CLI); the *EnvVar fields let users point KSail at non-standard environment variable names (mirrors the AWS/GCP/Hetzner/Omni pattern).

Field Type Default Description
subscriptionIdEnvVar string AZURE_SUBSCRIPTION_ID SubscriptionIDEnvVar is the environment variable containing the Azure subscription ID. Defaults to “AZURE_SUBSCRIPTION_ID”.
resourceGroupEnvVar string AZURE_RESOURCE_GROUP ResourceGroupEnvVar is the environment variable containing the Azure resource group that hosts the cluster. Defaults to “AZURE_RESOURCE_GROUP”. When neither the environment variable nor a configured value provides a resource group, cluster-scoped calls resolve it from the cluster’s ARM ID via a subscription-wide list, and Create requires it explicitly.

spec.provider.kubernetes (OptionsKubernetes)

Section titled “spec.provider.kubernetes (OptionsKubernetes)”

OptionsKubernetes defines options specific to the Kubernetes provider. The Kubernetes provider runs nested cluster nodes as pods inside an existing host cluster. It uses Gateway API (TCPRoute) to expose the nested cluster’s API server.

Field Type Default Description
kubeconfig string ~/.kube/config Kubeconfig is the path to the kubeconfig for the host cluster. Defaults to “~/.kube/config”.
kubeconfigEnvVar string KSAIL_HOST_KUBECONFIG KubeconfigEnvVar is the environment variable containing the host kubeconfig path. Defaults to “KSAIL_HOST_KUBECONFIG”.
context string Context is the kubeconfig context for the host cluster. When empty, uses the current context.
contextEnvVar string KSAIL_HOST_CONTEXT ContextEnvVar is the environment variable containing the host kubeconfig context. Defaults to “KSAIL_HOST_CONTEXT”.
gatewayClassName string GatewayClassName is the GatewayClass to use for exposing the nested API server. Must reference a GatewayClass that exists on the host cluster. When empty, the API is exposed via ClusterIP Service only (no external Gateway).
podCidr string 10.64.0.0/16 PodCIDR is the pod CIDR for the nested cluster. Must not overlap with the host cluster’s pod or service CIDRs. Defaults to “10.64.0.0/16”.
serviceCidr string 10.128.0.0/16 ServiceCIDR is the service CIDR for the nested cluster. Must not overlap with the host cluster’s pod or service CIDRs. Defaults to “10.128.0.0/16”.
persistence KubernetesPersistence Persistence defines storage persistence for the nested cluster’s data directory.

spec.provider.kubernetes.persistence (KubernetesPersistence)

Section titled “spec.provider.kubernetes.persistence (KubernetesPersistence)”

KubernetesPersistence defines storage persistence configuration for the Kubernetes provider. When enabled, a PVC is created for the nested cluster’s data directory to survive pod restarts. When disabled (default), emptyDir is used and clusters are fully ephemeral.

Field Type Default Description
enabled boolean Enabled controls whether a PVC is used for the nested cluster’s data directory. When false (default), emptyDir is used and the cluster is ephemeral.
storageClassName string StorageClassName is the StorageClass to use for the PVC. When empty, the cluster’s default StorageClass is used.
size string 20Gi Size is the storage request size for the PVC. Defaults to “20Gi”.
Field Type Default Description
sourceDirectory string k8s Path to the directory containing Kubernetes manifests. Used as the default path by validate, watch, and push when no explicit path argument is given.
validateOnPush boolean false Validate manifests against schemas before pushing (validation disabled by default)
tag string dev OCI artifact tag used for workload push and GitOps reconciliation (Flux OCIRepository and ArgoCD Application). Push priority: CLI oci:// ref > this field > registry-embedded tag > dev. Reconciliation priority: this field > registry-embedded tag > dev
kustomizationFile string Path to the kustomization directory relative to sourceDirectory. When set, Flux Sync.Path is configured to this path so Flux uses the specified kustomization as the entry point instead of requiring a root kustomization.yaml.
flux FluxConfig Flux bootstrap configuration: operator/distribution version pins and signature verification for the generated OCIRepository. Empty values use KSail’s pinned versions; a GitOps repo that declares these becomes the steady-state owner.
watch WatchConfig Configuration for the workload watch command (pre-apply hooks, etc.)
validation ValidationConfig Configuration for the workload validate command (additional kinds to skip, etc.).
scan ScanConfig Configuration for the workload scan command (Kubescape exceptions, frameworks, compliance threshold) so ‘ksail workload scan’ (no args) can act as a turnkey CI gate.
Field Type Default Description
model string Chat model (empty or ‘auto’ for API default)
reasoningEffort string Reasoning effort level for chat responses (low, medium, or high)

KSail references distribution-specific configuration files to customize cluster behavior. The path to these files is set via spec.cluster.distributionConfig.

Vanilla (implemented with Kind) Configuration

Section titled “Vanilla (implemented with Kind) Configuration”

Default: kind.yaml

See Kind Configuration for the full schema.

Example:

kind.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000
hostPort: 30000

Default: k3d.yaml

See K3d Configuration for the full schema.

Example:

k3d.yaml
apiVersion: k3d.io/v1alpha5
kind: Simple
servers: 1
agents: 2
ports:
- port: 8080:80
nodeFilters:
- loadbalancer

Default: talos/ directory

Talos uses a directory structure for Talos machine configuration patches. Place YAML patch files in talos/cluster/ (all nodes), talos/control-planes/, or talos/workers/:

# talos/cluster/kubelet.yaml (applies to all nodes)
machine:
kubelet:
extraArgs:
max-pods: "250"

See Talos Configuration Reference for patch syntax. Use spec.cluster.talos to configure node counts:

spec:
cluster:
distribution: Talos
distributionConfig: talos
talos:
controlPlanes: 3
workers: 2

On macOS, Docker runs in a Linux VM, so MetalLB virtual IPs are not accessible from the host. Use extraPortMappings to expose container ports directly:

spec:
cluster:
distribution: Talos
talos:
extraPortMappings:
- containerPort: 80
hostPort: 8080
protocol: TCP
- containerPort: 443
hostPort: 8443
protocol: TCP

Access services at http://localhost:8080. Ports are exposed on the first control-plane node; in multi-control-plane clusters, extraPortMappings apply only to that node.

KSail provides a JSON Schema for IDE validation and autocompletion. Reference it at the top of your ksail.yaml:

# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.json
apiVersion: ksail.io/v1alpha1
kind: Cluster
spec:
# ...

IDEs with YAML language support (e.g., VS Code + Red Hat YAML extension) provide field autocompletion, inline docs, validation, and enum suggestions.