Skip to content

Cluster Provisioning

Create and manage local Kubernetes clusters with a single command. KSail:

Use --benchmark during cluster create or cluster update to show per-component install durations.

Terminal window
ksail project init --name my-cluster --distribution Vanilla --cni Cilium
ksail cluster create

The generated config files work directly with kind, k3d, talosctl, and vcluster — KSail is a superset.

The full lifecycle — create a real cluster, inspect it, deploy a workload, and tear it down:

Use --ttl to create a cluster that auto-destroys after a set duration. See Ephemeral Clusters for usage patterns and tips.

Terminal window
ksail cluster create --ttl 1h

ksail cluster update applies changes from ksail.yaml to a running cluster. Changes are classified as in-place (no disruption), reboot-required, recreate-required, or wipe-required. Use --dry-run to preview changes without applying them. Use --output json for a machine-readable diff in CI pipelines or MCP tools:

Terminal window
# Preview changes without applying
ksail cluster update --dry-run
# Machine-readable diff for CI gating or MCP consumption
ksail cluster update --dry-run --output json | jq .recreateRequired

ksail cluster diff compares the desired configuration in ksail.yaml against the live cluster state and reports any drift — without applying changes. Use it in CI pipelines or monitoring workflows to catch unintended drift early.

Terminal window
# Report drift in human-readable form
ksail cluster diff
# Machine-readable output for CI gating
ksail cluster diff --output json | jq .recreateRequired
# Return exit code 2 when drift is detected (useful for CI gate steps)
ksail cluster diff --exit-code

Changes are classified the same way as cluster update: in-place, reboot-required, recreate-required, or wipe-required. Unlike cluster update --dry-run, cluster diff is a pure read operation — it never applies or stages any changes.

When a cluster has 3 or more nodes (control planes + workers ≥ 3), KSail automatically applies HA-ready defaults to supported Helm-based component installers. Below the 3-node threshold, no HA values are injected to avoid unschedulable pods on single-node or dual-node clusters.

Some charts ship a PodDisruptionBudget even without HA values. Below the threshold KSail relaxes those budgets, so draining a node during a Talos upgrade or node replacement can always finish. Today this applies to Gatekeeper, whose controller-manager budget is set to minAvailable: 0.

The possible HA settings are:

  • Replicas ≥ 2 — fast failover for controller workloads
  • PodDisruptionBudgets — minAvailable: 1 to prevent simultaneous evictions
  • Topology spread constraints — ScheduleAnyway by hostname to distribute pods across nodes

Each component receives the subset of these settings it supports. The table below is the source of truth for which settings are applied per component:

Component HA Setting
cert-manager controller + webhook replicas, PDB, topology spread
metrics-server replicas, PDB, topology spread
Kyverno admissionController replicas, PDB, topology spread
Gatekeeper replicas, PDB, topology spread
ArgoCD server + repoServer replicas, PDB
Cilium operator replicas
Calico Typha controlPlaneReplicas
kubelet-csr-approver replicas
cluster-autoscaler replicas
hcloud-ccm replicaCount
hetzner-csi controller replicaCount

When an external node autoscaler (such as Cluster Autoscaler, Karpenter, or Omni dynamic allocation) manages your node counts, set spec.cluster.autoscaler.node.enabled: true in ksail.yaml to prevent ksail cluster update from interfering.

By default (false), KSail reconciles spec.cluster.controlPlanes and spec.cluster.workers on every ksail cluster update. With autoscaling enabled, those diffs are skipped — the external autoscaler is the single source of truth for node counts. ksail cluster create is unaffected; initial node counts are still applied on first provision.

spec:
cluster:
autoscaler:
node:
enabled: true

For a KSail-managed Cluster Autoscaler on Talos with Hetzner, cluster diff also compares the installed Helm values with the values rendered by the current KSail version. A mismatch appears as cluster.autoscaler.node.chartValues; cluster update applies it even when ksail.yaml has not changed. KSail runs the same check for cert-manager when spec.cluster.certManager is Enabled, and reports a mismatch as cluster.certManager.chartValues, for the configured policy engine (Kyverno or Gatekeeper), reported as cluster.policyEngine.chartValues, for metrics-server when spec.cluster.metricsServer is Enabled on a distribution that does not ship its own, reported as cluster.metricsServer.chartValues, for Cilium when spec.cluster.cni is Cilium, reported as cluster.cni.cilium.chartValues, for the Hetzner CSI driver on Talos with Hetzner when spec.cluster.csi is not Disabled, reported as cluster.csi.chartValues, and for the Hetzner cloud controller manager that every Talos cluster on Hetzner runs, reported as cluster.loadBalancer.hcloudCCM.chartValues; its values also carry the spec.cluster.loadBalancer setting, so switching load balancers off reaches an existing controller this way. Argo CD is compared too when spec.cluster.gitOpsEngine is ArgoCD, reported as cluster.gitOpsEngine.argocd.chartValues; it carries the SOPS decryption and high-availability settings. The Flux operator chart takes no values from KSail, so there is nothing to compare there; Flux itself is configured through its FluxInstance. All of these operations use the resolved cluster context. Releases managed by Flux or Argo CD are excluded from this check and remain under GitOps control. That includes a release KSail installed that an Argo CD Application in the same cluster has since taken over: Argo CD leaves no mark on the Helm record, so KSail checks whether any Application in the same cluster tracks one of the release’s objects. Where the Argo CD API is installed, this needs permission to list applications.argoproj.io across namespaces. An Application whose destination names a cluster other than in-cluster also needs permission to list Secrets across namespaces, so KSail can read the Argo CD cluster secrets and see whether that name registers the cluster Argo CD runs in. If KSail cannot read the Applications, the cluster secrets or the release record, it stops with an error instead of changing the release.

KSail recognizes a Flux-owned release from its HelmRelease even when Helm’s storage labels do not identify Flux. This discovery requires permission to list HelmReleases across namespaces; if that ownership read fails, KSail reports the failure rather than treating an unreadable release as unmanaged. Clusters without the Flux HelmRelease API continue to use Helm’s storage labels.

If a release cannot be inspected, KSail prints a warning and continues with the other diff checks. Resolve that warning before treating the absence of a chart-values change as confirmation that the component’s settings match.

ksail cluster update reconciles the cluster’s distribution and Kubernetes versions declaratively on every run — there are no opt-in upgrade flags. Each version follows one of two rules:

  • Unset → follow latest. When you do not declare a version, KSail upgrades toward the latest stable version available in the OCI registry.
  • Set → pin. When you declare a version, KSail reconciles toward exactly that version (and never downgrades). Declare it in ksail.yaml (spec.cluster.kubernetesVersion and, for Talos, spec.cluster.talos.version) or per invocation with --kubernetes-version / --distribution-version. Precedence is flag > environment variable > config file > default.
Terminal window
# Follow the latest stable versions (nothing pinned)
ksail cluster update
# Pin a specific Kubernetes version for this run
ksail cluster update --kubernetes-version v1.34.0
# Pin the Talos OS version
ksail cluster update --distribution-version v1.13.3

The same fields apply at creation time: ksail cluster create provisions at the declared version, or at KSail’s built-in default (the latest version this KSail build supports) when unset.

Upgrade strategy by distribution:

Distribution Strategy Details
Talos Rolling upgrade Upgrades nodes in-place via the Talos API — no downtime for multi-node clusters
Vanilla (Kind) Recreation Cluster is deleted and recreated at the target version
K3s (K3d) Recreation Cluster is deleted and recreated at the target version
VCluster Recreation Cluster is deleted and recreated using KSail’s embedded vCluster/Kubernetes defaults; versions only advance when those defaults change (for example, after updating KSail)
KWOK Not supported KWOK has no version reconciliation; the controller image is pinned to the version bundled with KSail

For Talos (rolling upgrades), KSail steps through intermediate versions sequentially before reaching the final target. For recreation-based distributions, KSail skips intermediate versions and recreates the cluster once at the target version. You will be prompted for confirmation before any recreation; pass --force or --yes (-y) to skip the prompt.

ksail cluster