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 cluster 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}"
SyntaxVariable SetVariable Not Set
${VAR}Uses valueEmpty string + warning
${VAR:-default}Uses valueUses default (no warning)
${VAR:-}Uses valueEmpty 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}
spec:
cluster:
localRegistry:
registry: "${REGISTRY_USER}:${REGISTRY_PASS}@${REGISTRY_HOST:-ghcr.io}/myorg/myrepo"
Terminal window
export REGISTRY_USER="github-user"
export REGISTRY_PASS="ghp_secrettoken123"
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
FieldTypeRequiredDescription
apiVersionstringYesMust be ksail.io/v1alpha1
kindstringYesMust be Cluster
specobjectYesCluster and workload specification (see below)

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

FieldTypeDefaultDescription
editorstringEditor command for interactive workflows (e.g. code —wait)
clusterClusterSpec
providerProviderSpec
workloadWorkloadSpec
chatChatSpec

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

FieldTypeDefaultDescription
distributionConfigstring
connectionConnection
distributionenum
providerenum
cnienum
csienum
cdienum
metricsServerenum
loadBalancerenum
certManagerenum
policyEngineenum
localRegistryLocalRegistry
gitOpsEngineenum
sopsSOPS
nodeAutoscalingenumDeprecated. Use autoscaler.node.enabled instead. Do not set both nodeAutoscaling and autoscaler.
autoscalerAutoscalerConfigPod and node autoscaling configuration (supersedes deprecated nodeAutoscaling)
importImagesstringPath to tar archive with container images to import after cluster creation but before component installation
controlPlanesint321Number of control-plane nodes to create for the cluster (provider/distribution-agnostic)
workersint32Number of worker nodes to create for the cluster (provider/distribution-agnostic)
kubernetesVersionstringKubernetes version to deploy (Talos distribution). When unset: cluster update preserves the running version and new clusters use a default compatible with the pinned Talos version.
oidcOIDCSpecOIDC authentication configuration for the API server and kubeconfig
vanillaOptionsVanilla
talosOptionsTalos

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)

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)

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

See Distribution Configuration below for details on each format.

FieldTypeDefaultDescription
kubeconfigstring~/.kube/configPath to kubeconfig file
contextstring(derived)Kubeconfig context name
timeoutdurationTimeout 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

Hetzner options (spec.provider.hetzner):

  • controlPlaneServerType – Server type for control-plane nodes (default: cx23)
  • workerServerType – Server type for worker nodes (default: cx23)
  • location – Datacenter location: fsn1, nbg1, hel1 (default: fsn1)
  • networkName – Private network name (default: <cluster>-network)
  • networkCidr – Network CIDR block (default: 10.0.0.0/16)
  • sshKeyName – SSH key name for server access (optional)
  • tokenEnvVar – Environment variable for API token (default: HCLOUD_TOKEN)
  • fallbackLocations – Alternative locations to try when the primary is at capacity (default: nbg1, hel1)

Vanilla options (spec.cluster.vanilla):

  • mirrorsDir – Directory for containerd host mirror configuration
FieldTypeDefaultDescription
sourceDirectorystringk8sPath to the directory containing Kubernetes manifests. Used as the default path by validate, watch, and push when no explicit path argument is given.
validateOnPushbooleanfalseValidate manifests against schemas before pushing (validation disabled by default)
tagstringdevOCI 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
kustomizationFilestringPath 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.
watchWatchConfigConfiguration for the workload watch command (pre-apply hooks, etc.)
FieldTypeDefaultDescription
modelstringChat model (empty or ‘auto’ for API default)
reasoningEffortstringReasoning 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.