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
importImagesstringPath to tar archive with container images to import after cluster creation but before component installation
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

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

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)

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)

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)
  • iso – Cloud provider ISO/image ID for Talos Linux (default: 122630 for x86; use 122629 for ARM)

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)

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.
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.