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.
What is ksail.yaml?
Section titled “What is ksail.yaml?”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.
Environment Variable Expansion
Section titled “Environment Variable Expansion”KSail supports environment variable expansion in all string configuration values using the ${VAR_NAME} syntax for secure credentials, environment-specific paths, and dynamic values.
Syntax
Section titled “Syntax”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}"Expansion Behavior
Section titled “Expansion Behavior”| 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 parsingkind: ClusterapiVersion: kind.x-k8s.io/v1alpha4containerdConfigPatches: - |- [plugins."io.containerd.grpc.v1.cri".registry.mirrors."${REGISTRY:-localhost:5000}"] endpoint = ["http://${REGISTRY:-localhost:5000}"]# talos/cluster/registry.yaml - Environment variables are expandedmachine: 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 timemachine: registries: config: registry.example.com: auth: username: ${REGISTRY_USER} # The common placeholder maps to the effective cluster token source. password: ${REGISTRY_TOKEN:-forbidden-fallback}Example: Credentials
Section titled “Example: Credentials”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_TOKENResolution is deterministic and registry-agnostic — no registry host is special-cased:
- CLI and publish paths read
cliTokenEnvVar, falling back totokenEnvVaronly 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 totokenEnvVarthe 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
registryis 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.
export REGISTRY_USER="registry-user"export REGISTRY_PUBLISH_TOKEN="write-capable-token"export REGISTRY_PULL_TOKEN="read-only-token"ksail cluster createExample: Multi-Environment Setup
Section titled “Example: Multi-Environment Setup”spec: cluster: connection: context: "${CLUSTER_NAME:-kind-kind}" distributionConfig: "${ENV:-dev}/kind.yaml" workload: sourceDirectory: "${ENV:-dev}/k8s"# Development (using defaults)ksail cluster create
# Production (override with environment variables)export ENV="prod"export CLUSTER_NAME="prod-cluster"ksail cluster createMinimal Example
Section titled “Minimal Example”# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.jsonapiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: distribution: Vanilla distributionConfig: kind.yamlThis minimal configuration creates a Vanilla cluster (implemented with Kind) using defaults for all other settings.
Complete Example
Section titled “Complete Example”# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.jsonapiVersion: ksail.io/v1alpha1kind: Clusterspec: 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: trueConfiguration Reference
Section titled “Configuration Reference”Top-Level Fields
Section titled “Top-Level Fields”| 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). |
spec.editor
Section titled “spec.editor”Editor command for interactive workflows (e.g., code --wait, vim). Falls back to SOPS_EDITOR, KUBE_EDITOR, EDITOR, VISUAL, or system defaults.
spec.cluster (ClusterSpec)
Section titled “spec.cluster (ClusterSpec)”| 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. |
distribution
Section titled “distribution”See Distributions for detailed information.
Vanilla(default) – Standard upstream Kubernetes via KindK3s– Lightweight Kubernetes via K3dTalos– Talos Linux in Docker containers or Hetzner Cloud serversVCluster– Virtual clusters via vClusterKWOK– Simulated clusters via KWOK (control-plane only, no real workloads)EKS– Amazon Elastic Kubernetes Service via eksctl (requires AWS credentials and theeksctlCLI onPATH)GKE– Google Kubernetes Engine via the native Go SDK (requires Google Cloud Application Default Credentials and a project viaGOOGLE_CLOUD_PROJECT)AKS– Azure Kubernetes Service via the native Go SDK (requires Azure credentials and a subscription viaAZURE_SUBSCRIPTION_ID)
provider
Section titled “provider”See Providers for more details.
Docker(default) – Run nodes as Docker containers (local development)Hetzner– Run nodes on Hetzner Cloud servers (requiresHCLOUD_TOKEN)Omni– Manage Talos cluster nodes through Sidero OmniAWS– 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)
distributionConfig
Section titled “distributionConfig”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:
Vanilla→kind.yamlK3s→k3d.yamlTalos→talos/(directory)VCluster→vcluster.yamlKWOK→kwok/(directory)EKS→eks.yamlGKE→gke.yaml(optional – the GKE API owns the cluster shape)AKS→aks.yaml(optional – the AKS API owns the cluster shape)
See Distribution Configuration below for details on each format.
connection (Connection)
Section titled “connection (Connection)”| 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:
Vanilla→kind-kindK3s→k3d-k3d-defaultTalos(Docker/Hetzner) →admin@talos-defaultTalos(Omni) → the context name generated by Omni (e.g.,devantler-prod)VCluster→vcluster-docker_vcluster-defaultKWOK→kwok-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 (kindnetdfor Vanilla,flannelfor K3s)Cilium– Installs Cilium for advanced networking and observabilityCalico– 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)
metricsServer
Section titled “metricsServer”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-serverDisabled– Skip installation
When metrics-server is enabled on Vanilla or Talos, KSail automatically:
- Configures kubelet certificate rotation (
serverTLSBootstrap: true) - Installs kubelet-csr-approver to approve certificate requests
- Deploys metrics-server with secure TLS communication
certManager
Section titled “certManager”Whether to install cert-manager for TLS certificate management.
Enabled– Install cert-managerDisabled(default) – Skip installation
policyEngine
Section titled “policyEngine”Policy engine to install for enforcing security, compliance, and best practices. See Policy Engines for details.
None(default) – No policy engineKyverno– Install KyvernoGatekeeper– Install OPA Gatekeeper
localRegistry
Section titled “localRegistry”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 registryghcr.io/myorg/myrepo– GitHub Container Registry${USER}:${PASS}@ghcr.io:443/myorg– With credentials from environment variables
gitOpsEngine
Section titled “gitOpsEngine”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 engineFlux– Install Flux CD and scaffold FluxInstance CRArgoCD– Install Argo CD and scaffold Application CR
Distribution and Tool Options
Section titled “Distribution and Tool Options”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:125127for 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 updatekeeps the version already running (no unrequested upgrade) and new clusters default to one compatible with the pinnedtalos.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. |
spec.provider (ProviderSpec)
Section titled “spec.provider (ProviderSpec)”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. |
spec.provider.hetzner (OptionsHetzner)
Section titled “spec.provider.hetzner (OptionsHetzner)”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. |
spec.provider.omni (OptionsOmni)
Section titled “spec.provider.omni (OptionsOmni)”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. |
spec.provider.aws (OptionsAWS)
Section titled “spec.provider.aws (OptionsAWS)”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”. |
spec.provider.gcp (OptionsGCP)
Section titled “spec.provider.gcp (OptionsGCP)”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”. |
spec.provider.azure (OptionsAzure)
Section titled “spec.provider.azure (OptionsAzure)”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”. |
spec.workload (WorkloadSpec)
Section titled “spec.workload (WorkloadSpec)”| 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. |
spec.chat (ChatSpec)
Section titled “spec.chat (ChatSpec)”| 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) |
Distribution Configuration
Section titled “Distribution Configuration”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: ClusterapiVersion: kind.x-k8s.io/v1alpha4nodes: - role: control-plane extraPortMappings: - containerPort: 30000 hostPort: 30000K3s (implemented with K3d) Configuration
Section titled “K3s (implemented with K3d) Configuration”Default: k3d.yaml
See K3d Configuration for the full schema.
Example:
apiVersion: k3d.io/v1alpha5kind: Simpleservers: 1agents: 2ports: - port: 8080:80 nodeFilters: - loadbalancerTalos Configuration
Section titled “Talos Configuration”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: 2Port Mappings (Docker Provider)
Section titled “Port Mappings (Docker Provider)”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: TCPAccess 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.
Schema Support
Section titled “Schema Support”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.jsonapiVersion: ksail.io/v1alpha1kind: Clusterspec: # ...IDEs with YAML language support (e.g., VS Code + Red Hat YAML extension) provide field autocompletion, inline docs, validation, and enum suggestions.