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 the cluster and workload configuration. Run ksail cluster init to generate this file, which can be committed to version control and shared with your team.

The configuration file uses the ksail.io/v1alpha1 API version and follows the Cluster kind schema. It defines:

  • Cluster settings: distribution, networking, components
  • Connection details: kubeconfig path, context, timeouts
  • Workload configuration: manifest directory, validation preferences
  • Editor preferences: for interactive workflows

KSail supports environment variable expansion in all string configuration values using the ${VAR_NAME} syntax. This enables:

  • Secure credential management: Keep sensitive values out of version control
  • Environment-specific configuration: Use different values per environment (dev/staging/prod)
  • Dynamic path resolution: Reference user-specific or system-specific paths

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}"
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:

  1. ksail.yaml — All supported string fields (see below)
  2. Distribution configskind.yaml, k3d.yaml file contents
  3. Talos patches — All YAML patch files in talos/cluster/, talos/control-planes/, talos/workers/

This means you can use ${VAR} syntax inside your Kind, K3d, or Talos configuration files too:

# 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}

Environment variable expansion works in these string fields:

General:

  • spec.editor - Editor command
  • spec.cluster.connection.kubeconfig - Kubeconfig path
  • spec.cluster.connection.context - Kubernetes context name
  • spec.cluster.distributionConfig - Distribution config path
  • spec.cluster.localRegistry.registry - Registry specification (including credentials)
  • spec.workload.sourceDirectory - Workload manifest directory
  • spec.chat.model - Chat model name

Distribution-specific:

  • spec.cluster.vanilla.mirrorsDir - Mirror configuration directory
  • spec.cluster.talos.config - Talos configuration path

Provider-specific:

  • spec.cluster.hetzner.sshKeyName - SSH key name
  • spec.cluster.hetzner.networkName - Network name
  • spec.cluster.hetzner.placementGroup - Placement group name
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

Example: Distribution Config with Variables

Section titled “Example: Distribution Config with Variables”

Environment variables also work inside distribution configuration files:

kind.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: ${INGRESS_PORT:-80}
hostPort: ${HOST_PORT:-8080}
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."${MIRROR_REGISTRY:-docker.io}"]
endpoint = ["http://${REGISTRY:-localhost:5000}"]
talos/cluster/registry.yaml
machine:
registries:
mirrors:
docker.io:
endpoints:
- http://${REGISTRY:-localhost:5000}
# 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
workloadWorkloadSpec
chatChatSpec

Editor command used by KSail for interactive workflows like ksail cipher edit or ksail workload edit.

Examples: code --wait, vim, nano

If not specified, KSail falls back to standard editor environment variables (SOPS_EDITOR, KUBE_EDITOR, EDITOR, VISUAL) or system defaults (vim, nano, vi).

FieldTypeDefaultDescription
distributionConfigstring
connectionConnection
distributionenum
providerenum
cnienum
csienum
metricsServerenum
loadBalancerenum
certManagerenum
policyEngineenum
localRegistryLocalRegistry
gitOpsEngineenum
importImagesstringPath to tar archive with container images to import after cluster creation but before component installation
vanillaOptionsVanilla
talosOptionsTalos
hetznerOptionsHetzner
omniOptionsOmni

Valid values:

  • Vanilla (default)
  • K3s
  • Talos
  • VCluster

See Distributions for detailed information about each distribution.

  • Vanilla – Standard upstream Kubernetes (implemented with Kind)
  • K3s – Lightweight Kubernetes (implemented with K3d)
  • TalosTalos Linux in Docker containers or Hetzner Cloud servers

Valid values:

  • Docker (default)
  • Hetzner
  • Omni

See Providers for more details.

  • Docker – Run nodes as Docker containers (local development)
  • Hetzner – Run nodes on Hetzner Cloud servers (requires HCLOUD_TOKEN)

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
  • Talosadmin@talos-default

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

Valid values:

  • Default (default)
  • Cilium
  • Calico

See CNI for more details.

  • 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

Valid values:

  • Default (default)
  • Enabled
  • Disabled

See CSI for more details.

  • 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)

Valid values:

  • Default (default)
  • Enabled
  • Disabled

Whether to install metrics-server for resource metrics.

  • 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

Valid values:

  • Enabled
  • Disabled (default)

Whether to install cert-manager for TLS certificate management.

Valid values:

  • None (default)
  • Kyverno
  • Gatekeeper

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

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

Valid values:

  • None (default)
  • Flux
  • ArgoCD

GitOps engine to install for continuous deployment workflows. See GitOps for more details about Flux and ArgoCD. When set to Flux or ArgoCD, KSail scaffolds a GitOps CR (FluxInstance or ArgoCD Application) into your source directory.

  • None – 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)

Hetzner options (spec.cluster.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
sourceDirectorystringk8s
validateOnPushboolean
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

The Vanilla distribution is configured via a YAML file following the Kind configuration schema. This allows you to customize:

  • Node images and versions
  • Extra port mappings
  • Extra mounts
  • Networking settings

Documentation: Kind Configuration

Example:

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

Default: k3d.yaml

The K3s distribution is configured via a YAML file following the K3d configuration schema. This allows you to customize:

  • Server and agent counts
  • Port mappings
  • Volume mounts
  • Registry configurations

Documentation: K3d Configuration

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. Each directory contains YAML patch files that modify the Talos machine configuration.

Documentation: Talos Configuration Reference

Directory structure and examples:

talos/cluster/kubelet.yaml
machine:
kubelet:
extraArgs:
max-pods: "250"
talos/control-planes/api.yaml
machine:
kubelet:
extraArgs:
feature-gates: "EphemeralContainers=true"
talos/workers/custom.yaml
machine:
sysctls:
net.core.somaxconn: "65535"

Use spec.cluster.talos to configure node counts:

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

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 (like VS Code with the Red Hat YAML extension) will provide:

  • Field autocompletion
  • Inline documentation
  • Validation errors for invalid values
  • Enum suggestions for fields like distribution, cni, etc.