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: cx33
          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: cx33
          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: cx33
          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: cpx31
          image: ubuntu-24.04
          location: fsn1
          ssh-key: [0]
          network: 0
          label: lok8s.dev/cluster=production,lok8s.dev/role=worker
        - name: worker-2
          type: cpx31
          image: ubuntu-24.04
          location: fsn1
          ssh-key: [0]
          network: 0
          label: lok8s.dev/cluster=production,lok8s.dev/role=worker

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