Skip to content

Creating Kubernetes Clusters on Hetzner with KSail and Talos

Talos Linux on Hetzner Cloud

Setting up Kubernetes environments doesn’t have to be expensive or complicated. With Hetzner Cloud’s affordable pricing, Talos Linux’s security-focused immutable OS, and KSail’s unified tooling, you can have a cluster running in minutes. This post walks through the complete setup.

Hetzner Cloud offers some of the most affordable cloud VPS servers in the industry. A capable cx23 server (2 vCPU, 4GB RAM) costs around €3.74/month — significantly cheaper than equivalent offerings from AWS, GCP, or Azure.

Talos Linux is a minimal, immutable operating system designed specifically for Kubernetes. There’s no SSH, no shell, no package manager — just the Talos API and Kubernetes. This dramatically reduces the attack surface and eliminates configuration drift.

KSail brings it all together with a single binary that handles cluster provisioning, GitOps setup, and workload management. Instead of juggling hcloud, talosctl, kubectl, helm, and flux commands, you use one consistent interface.

If you don’t already have a Hetzner account:

  1. Go to Hetzner Cloud Console
  2. Click “Register” and complete the signup process
  3. Verify your email and complete any required identity verification

Hetzner has documentation for getting started: Account Getting Started Guide.

Projects in Hetzner Cloud are organizational units that contain your resources (servers, networks, load balancers, etc.). Each project has its own API tokens and billing.

  1. Log into the Hetzner Cloud Console
  2. Click “New Project” in the sidebar
  3. Name your project (e.g., “kubernetes-dev” or “my-homelab”)
  4. Click “Create”

KSail needs an API token to provision and manage Hetzner Cloud resources.

  1. In your project, go to SecurityAPI Tokens
  2. Click Generate API Token
  3. Name the token (e.g., “ksail-cluster-management”)
  4. Select Read & Write permissions — KSail needs to create and delete servers
  5. Click Generate API Token
  6. Copy the token immediately — you won’t be able to see it again!

For more details, see Hetzner’s Generating API Token Guide.

KSail reads the Hetzner API token from the HCLOUD_TOKEN environment variable. Add it to your shell configuration:

Terminal window
# For the current session
export HCLOUD_TOKEN="your-api-token-here"
# To persist across sessions, add to your shell config
echo 'export HCLOUD_TOKEN="your-api-token-here"' >> ~/.zshrc # or ~/.bashrc
source ~/.zshrc

You can verify the token is set:

Terminal window
echo $HCLOUD_TOKEN | head -c 10 # Should show first 10 characters

KSail is distributed as a single binary. The easiest installation method is via Homebrew:

Terminal window
brew install --cask devantler-tech/tap/ksail

Alternatively, if you have Go installed:

Terminal window
go install github.com/devantler-tech/ksail/v5@latest

Verify the installation:

Terminal window
ksail --version

KSail’s init command scaffolds a complete project structure. For a basic Talos cluster on Hetzner with Cilium CNI:

Terminal window
mkdir my-cluster && cd my-cluster
ksail cluster init --distribution Talos --provider Hetzner --cni Cilium

This creates ksail.yaml (cluster configuration), talos/ (Talos configs), and k8s/ (your Kubernetes manifests).

For GitOps workflows, add a GitOps engine and external registry:

Terminal window
ksail cluster init \
--distribution Talos \
--provider Hetzner \
--cni Cilium \
--gitops-engine Flux \
--local-registry '${GITHUB_USER}:${GITHUB_TOKEN}@ghcr.io/your-org/your-cluster'

This configures Flux to sync manifests from GitHub Container Registry. See Step 9: Deploying Workloads for the full GitOps workflow.

For all available flags and configuration options, see the KSail documentation:

With your configuration ready, create the cluster:

Terminal window
ksail cluster create

This command:

  1. Creates servers in Hetzner Cloud
  2. Configures the private network
  3. Bootstraps Talos Linux on each node
  4. Initializes the Kubernetes cluster
  5. Installs your selected CNI, CSI, and other components
  6. Configures your local kubeconfig
  7. Configures your local talosconfig

The process takes 3-5 minutes depending on your cluster size.

You can watch the progress as KSail outputs status updates for each stage.

Once your cluster is running, KSail provides commands for common operations:

Terminal window
ksail cluster info # Show cluster status
ksail cluster list # List all KSail-managed clusters
ksail cluster connect # Open K9s for interactive management
ksail cluster stop # Stop the cluster
ksail cluster start # Start a stopped cluster

Your kubeconfig is automatically configured, so standard kubectl commands work too.

For the full command reference, see Cluster Commands.

KSail wraps kubectl and GitOps operations under the workload command. For cloud clusters like Hetzner, you have two main options for deploying workloads.

The simplest approach applies manifests directly to the cluster:

Terminal window
ksail workload apply -k ./k8s # Apply Kustomize manifests
ksail workload get pods # Check pod status
ksail workload logs deployment/my-app # View logs

This works well for quick iterations but doesn’t provide GitOps benefits like drift detection and automatic reconciliation.

For cloud clusters without a local Docker registry, you can use an external OCI registry like GitHub Container Registry (ghcr.io). This enables full GitOps workflows with Flux or ArgoCD.

Step 1: Create a GitHub Personal Access Token

  1. Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
  2. Create a token with write:packages and read:packages scopes
  3. Export the credentials:
Terminal window
export GITHUB_USER="your-username"
export GITHUB_TOKEN="ghp_your-token-here"

Step 2: Initialize with GitOps and External Registry

When scaffolding your cluster, specify the GitOps engine and external registry:

Terminal window
ksail cluster init \
--distribution Talos \
--provider Hetzner \
--cni Cilium \
--gitops-engine Flux \
--local-registry '${GITHUB_USER}:${GITHUB_TOKEN}@ghcr.io/your-org/your-cluster'

The --local-registry flag accepts the format [user:pass@]host[:port][/path]. Environment variable placeholders like ${GITHUB_TOKEN} are expanded at runtime, keeping credentials out of your config files.

Step 3: Create the Cluster

Terminal window
ksail cluster create

This installs Flux and configures it to sync from your external registry.

Step 4: Push and Reconcile Workloads

Terminal window
# Package manifests and push to registry
ksail workload push
# Trigger GitOps reconciliation
ksail workload reconcile

The push command packages your k8s/ directory as an OCI artifact and pushes it to ghcr.io. Flux then pulls and applies the manifests automatically.

For the full workload command reference, see Workload Commands.

When you’re done with the cluster:

Terminal window
ksail cluster delete

This removes:

  • All Hetzner Cloud servers
  • The private network
  • Placement groups
  • Local kubeconfig entries
  • Local talosconfig entries

A minimal development setup (1 control plane) using a cx23 server costs under €4/month.

For a more robust setup (3 control planes + 2 workers) using cx23 servers:

ComponentMonthly Cost
3x cx23 control planes€11.22
2x cx23 workers€7.48
Private networkFree
Total~€18.70/month

This is remarkably affordable for a Kubernetes development cluster.

Explore the KSail documentation for advanced topics including secret management with SOPS, mirror registries, and GitOps workflows.

Integration with Hetzner Cloud Controller Manager and Hetzner Cloud CSI Driver is planned for future releases. This will enable:

  • Cloud Load Balancers: Automatic provisioning of Hetzner Load Balancers for Kubernetes Services of type LoadBalancer
  • Persistent Storage: Dynamic provisioning of Hetzner Cloud Volumes for PersistentVolumeClaims

This is the first iteration of Hetzner Cloud support in KSail. If you encounter bugs or find missing features, please open an issue on GitHub. Your feedback helps improve the tool for everyone.

Testing and maintaining cloud provider integrations comes with ongoing infrastructure costs. Hetzner is supported because I use it for my own homelab and want to manage it via KSail. Additional cloud providers (AWS, GCP, Azure, etc.) will not be added without sponsorship to cover testing costs.

If you’d like to see your preferred cloud provider supported, consider sponsoring the project on GitHub.


This blog post was written with the assistance of GitHub Copilot and Claude Opus 4.5. The content is based on real-world experience running Talos development clusters on Hetzner Cloud.