Multi-Environment Workflows with --config
KSail supports two complementary approaches for multi-environment workflows:
- Separate config files (
--config) β a dedicatedksail.<env>.yamlper environment with different settings - Environment variable expansion β a single shared
ksail.yamlwith${VAR}placeholders substituted at runtime
You can use either approach or combine both. This guide walks through common patterns.
Approach 1 β Separate Config Files (--config)
Section titled βApproach 1 β Separate Config Files (--config)βThe --config flag overrides KSailβs default config file discovery (which walks up the directory tree looking for ksail.yaml). Pair it with environment-specific files to manage distinct clusters from the same project root.
Project Layout
Section titled βProject Layoutβmy-app/βββ ksail.yaml # shared defaults (optional, not used directly)βββ ksail.dev.yaml # local dev clusterβββ ksail.staging.yaml # staging cluster on Hetznerβββ ksail.prod.yaml # production clusterβββ k8s/β βββ kustomization.yamlβ βββ ...βββ kind.yaml # Kind cluster config (used by ksail.dev.yaml)βββ talos/ # Talos configs (used by ksail.staging.yaml / ksail.prod.yaml)Example Config Files
Section titled βExample Config Filesβksail.dev.yaml β local Kind cluster with Flux and a local registry:
# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.jsonapiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: name: dev distribution: Vanilla gitOpsEngine: Flux cni: Default localRegistry: registry: localhost:5050 metricsServer: Default workload: sourceDirectory: k8sksail.staging.yaml β Talos cluster on Hetzner with Cilium and Flux:
# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.jsonapiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: name: staging distribution: Talos provider: Hetzner gitOpsEngine: Flux cni: Cilium metricsServer: Enabled workload: sourceDirectory: k8sksail.prod.yaml β production Talos cluster on Omni with all security components:
# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.jsonapiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: name: production distribution: Talos provider: Omni gitOpsEngine: Flux cni: Cilium policyEngine: Kyverno certManager: Enabled metricsServer: Enabled workload: sourceDirectory: k8s# Work on the local dev clusterksail --config ksail.dev.yaml cluster createksail --config ksail.dev.yaml workload watch
# Validate manifests against staging constraints before promotingksail --config ksail.staging.yaml workload validate
# Push manifests to the OCI registry for production GitOpsksail --config ksail.prod.yaml workload push
# Update a running cluster to match the latest configksail --config ksail.staging.yaml cluster update
# Tear down the dev cluster after a sessionksail --config ksail.dev.yaml cluster deleteApproach 2 β Shared Config with Environment Variables
Section titled βApproach 2 β Shared Config with Environment VariablesβUse a single ksail.yaml with ${VAR} placeholders. KSail expands them at runtime from your shell environment. This keeps one file in version control while allowing machine- or environment-specific values.
# yaml-language-server: $schema=https://raw.githubusercontent.com/devantler-tech/ksail/main/schemas/ksail-config.schema.jsonapiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: name: "${CLUSTER_NAME:-dev}" distribution: "${CLUSTER_DISTRIBUTION:-Vanilla}" provider: "${CLUSTER_PROVIDER:-Docker}" gitOpsEngine: Flux cni: "${CLUSTER_CNI:-Default}" localRegistry: registry: "${LOCAL_REGISTRY:-localhost:5050}" workload: sourceDirectory: "${WORKLOAD_DIR:-k8s}"Switch environments by exporting different variable sets:
# Local dev (default values used)ksail cluster create
# Stagingexport CLUSTER_NAME=stagingexport CLUSTER_DISTRIBUTION=Talosexport CLUSTER_PROVIDER=Hetznerexport CLUSTER_CNI=Ciliumksail cluster update
# CI (inline)CLUSTER_NAME=ci CLUSTER_DISTRIBUTION=K3s ksail cluster create.env Files (Optional)
Section titled β.env Files (Optional)βFor convenience, manage per-environment variables in .env files and source them before running KSail:
export CLUSTER_NAME=devexport CLUSTER_DISTRIBUTION=Vanillaexport LOCAL_REGISTRY=localhost:5050export CLUSTER_NAME=stagingexport CLUSTER_DISTRIBUTION=Talosexport CLUSTER_PROVIDER=Hetznerexport CLUSTER_CNI=Ciliumsource .env.staging && ksail cluster updateApproach 3 β Combining Both
Section titled βApproach 3 β Combining BothβUse --config for structural differences between environments (different distributions, providers, or components) and environment variables within each config file for credentials and machine-specific paths:
ksail.dev.yaml:
apiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: name: dev distribution: Vanilla localRegistry: registry: "${DEV_REGISTRY:-localhost:5050}"ksail.prod.yaml:
apiVersion: ksail.io/v1alpha1kind: Clusterspec: cluster: name: production distribution: Talos provider: Omni omni: endpoint: "${OMNI_ENDPOINT}" localRegistry: registry: "${PROD_REGISTRY}"# Dev: env vars provide the registry URLDEV_REGISTRY=localhost:5050 ksail --config ksail.dev.yaml cluster create
# Prod: env vars provide the Omni endpoint and external registryOMNI_ENDPOINT=https://my-org.omni.siderolabs.io:443 \ PROD_REGISTRY=ghcr.io/myorg/myrepo \ ksail --config ksail.prod.yaml workload pushCI/CD Usage
Section titled βCI/CD UsageβPass --config as an argument to the ksail-cluster composite action:
- uses: devantler-tech/ksail/.github/actions/ksail-cluster@main with: config: ksail.staging.yaml # path to the environment-specific config push: "true" # push manifests for GitOps reconcile: "true" # trigger GitOps reconcile step env: OMNI_ENDPOINT: ${{ secrets.OMNI_ENDPOINT }}For ephemeral test clusters in CI, combine --config with --ttl as a safety net:
ksail --config ksail.ci.yaml cluster create --ttl 30m# run tests...ksail --config ksail.ci.yaml cluster deleteSee Ephemeral Clusters (βttl) for the full TTL guide, or PR Preview Clusters for the full action inputs reference and PR preview patterns.
-
Name your clusters after their environment (
dev,staging,production) so context names are predictable across machines. -
Keep distribution-specific config files (
kind.yaml,talos/) named consistently βksail cluster initgenerates them alongsideksail.yaml. -
Version-control all
ksail.<env>.yamlfiles but keep credentials in environment variables or a secret manager. -
Use
ksail workload validatewith the target environment config before promoting manifests. See Substitution Expansion (validate) for how Flux${VAR}substitutions are handled during validation.Terminal window ksail --config ksail.staging.yaml workload validate -
Use
ksail cluster update --dry-runto preview the impact of config changes on a running cluster before applying them.