Skip to content

KubeOne on Hetzner ​

Set up a production Kubernetes cluster using KubeOne as the provisioner.

Prerequisites ​

  • lok8s CLI installed
  • hcloud CLI installed and authenticated
  • An SSH key pair added to your Hetzner project
  • Hetzner Cloud API token exported as HCLOUD_TOKEN

Cluster configuration ​

Create clusters/example.com/cluster.lok8s.yaml with kind: KubeOne (the folder is named after your domain), then run lo use example.com. The machines are declared in the Hetzner provider descriptor: one server entry per node, with the role in the lok8s.dev/role label. See First Cluster for every key.

yaml
# clusters/example.com/cluster.lok8s.yaml
apiVersion: cluster.lok8s.dev/v1beta1
kind: KubeOne
metadata:
  name: production
spec:
  kubernetes:
    version: "v1.35.5"
  cluster:
    # REQUIRED: a domain you control; used for the API endpoint and cert names
    domain: example.com
  provider:
    name: hetzner
    config:
      cluster_name: production
      sshUser: root
      sshPrivateKey: ~/.ssh/id_ed25519
      sshPublicKey: ~/.ssh/id_ed25519.pub
      ssh-key:
        - name: production
          public-key-from-file: ~/.ssh/id_ed25519.pub
      network:
        - name: production
          ip-range: 10.0.0.0/16
          "#subnets":
            - network-zone: eu-central
              type: cloud
              ip-range: 10.0.0.0/24
      server:
        - name: cp-1
          type: cpx32
          image: ubuntu-24.04
          location: fsn1
          ssh-key: [0]
          network: 0
          label: lok8s.dev/cluster=production,lok8s.dev/role=control-plane
        - name: cp-2
          type: cpx32
          image: ubuntu-24.04
          location: fsn1
          ssh-key: [0]
          network: 0
          label: lok8s.dev/cluster=production,lok8s.dev/role=control-plane
        - name: cp-3
          type: cpx32
          image: ubuntu-24.04
          location: fsn1
          ssh-key: [0]
          network: 0
          label: lok8s.dev/cluster=production,lok8s.dev/role=control-plane
        - name: worker-1
          type: cpx32
          image: ubuntu-24.04
          location: fsn1
          ssh-key: [0]
          network: 0
          label: lok8s.dev/cluster=production,lok8s.dev/role=worker
        - name: worker-2
          type: cpx32
          image: ubuntu-24.04
          location: fsn1
          ssh-key: [0]
          network: 0
          label: lok8s.dev/cluster=production,lok8s.dev/role=worker

The example uses cpx32 (4 vCPU, 8 GB RAM) for every server. Any type with at least the same vCPU and RAM that your Hetzner account offers in the location works too.

Stock differs per account and changes by the hour: if Hetzner refuses a type, use another type or another location (fsn1, nbg1 or hel1).

Provision ​

bash
lo provision

This will:

  1. Create Hetzner Cloud servers for control plane and workers
  2. Set up the private network
  3. Install Kubernetes via KubeOne
  4. Write the kubeconfig to .kubeconfig/production.yaml in your project, named after metadata.name, mode 0600. Your ~/.kube/config is never touched.

The config above asks Hetzner for five servers (3 control plane and 2 workers), billed hourly at Hetzner’s published prices for those server types. Nothing here is one-way: lo destroy removes every server lok8s created, whenever you want.

Verify ​

kubectl still points at whatever context it used before, so select the new cluster explicitly; otherwise get nodes answers for your previous cluster:

bash
export KUBECONFIG=.kubeconfig/production.yaml
kubectl get nodes
# (AGE trimmed — VERSION is what to check)
# NAME       STATUS   ROLES           VERSION
# cp-1       Ready    control-plane   v1.35.5
# cp-2       Ready    control-plane   v1.35.5
# cp-3       Ready    control-plane   v1.35.5
# worker-1   Ready    <none>          v1.35.5
# worker-2   Ready    <none>          v1.35.5

HA considerations ​

  • Declare three control-plane servers for production
  • Control plane nodes are spread across fault domains automatically
  • The private network (network[].ip-range) isolates cluster traffic

Adding bootstrap addons ​

Cluster-infra addons (CNI, CCM, cert-manager, monitoring, …) are listed under spec.bootstrap and applied in order at provision time:

yaml
  bootstrap:
    - cilium
    - ccm
    - monitoring

Run lo bootstrap to apply bootstrap changes to an existing cluster; it re-applies spec.bootstrap and skips the infrastructure reconcile. lo provision does both and is safe to re-run too.

Next steps ​


Doc status ​

AspectDetail
Last reviewed5 September 2026