Want to run a lightweight Kubernetes cluster with high availability? Here’s a quick and easy step-by-step guide to get k3s running on Hetzner with two control plane nodes behind a load balancer and three worker nodes for your workloads.

Let’s get started! 🛠️


1. What We’re Building

We’ll create a cluster with:

  • 1 Load Balancer – handles traffic to your control planes
  • 2 Control Plane Nodes – run the k3s server with embedded etcd
  • 3 Worker Nodes – run your apps and services

Total: 6 resources on Hetzner (5 servers + 1 load balancer)


2. Spin Up Infrastructure on Hetzner

🖥️ Create the Servers

  • Go to the Hetzner Cloud Console
  • Launch 5 VPS (virtual machines):
    • 2 for control plane nodes
    • 3 for worker nodes

🌐 Set Up a Load Balancer

  • In Hetzner, create a Load Balancer
  • Add both control plane nodes as targets
  • Expose TCP port 6443 (Kubernetes API port)
  • Note down the load balancer’s public IP or hostname — we’ll need this later

🔒 (Optional) Create a Private Network

  • Set up a private network in Hetzner
  • Attach all servers + the load balancer to it
  • Ensure traffic on port 6443 is allowed between the load balancer and control planes

Tip: Private networks add security and faster internal communication — highly recommended!


3. Set Up the Control Plane Nodes

We’ll first initialize the cluster on one server, then join the second.

🔑 3.1 Generate a Cluster Token

On any machine with OpenSSL:

openssl rand -hex 16

This gives you a 32-character token. Copy it! You’ll use it on every node as the K3S_TOKEN.


🟢 3.2 Start the First Control Plane Node

SSH into the first control plane server and run:

curl -sfL https://get.k3s.io | \
  K3S_TOKEN=YOUR_SECRET_TOKEN sh -s - server \
  --cluster-init \
  --tls-san

Replace:

  • YOUR_SECRET_TOKEN with the token from earlier
  • with your actual load balancer address

After a minute or two, check if it’s running:

sudo kubectl get nodes

You should see this node listed!


🟢 3.3 Join the Second Control Plane Node

On the second control plane server, run:

curl -sfL https://get.k3s.io | \
  K3S_TOKEN=YOUR_SECRET_TOKEN sh -s - server \
  --server https://:6443 \
  --tls-san

This command joins it to the first node via the load balancer.


4. Add the Worker Nodes

Now for the workers. SSH into each worker and run:

curl -sfL https://get.k3s.io | \
  K3S_TOKEN=YOUR_SECRET_TOKEN sh -s - agent \
  --server https://:6443

Repeat for all 3 workers. Give it a minute, then check from a control plane node:

kubectl get nodes

You should now see all 5 nodes!


5. Check Everything is Working

Run this on either control plane node:

kubectl get nodes

You should see all your nodes listed as Ready.

Example output:

NAME            STATUS   ROLES         AGE   VERSION
control-1       Ready    control-plane  Xm   v1.29.x
control-2       Ready    control-plane  Xm   v1.29.x
worker-1        Ready             Xm   v1.29.x
worker-2        Ready             Xm   v1.29.x
worker-3        Ready             Xm   v1.29.x

6. That’s It! 🎉

Here’s what you’ve just built:

✅ A Highly Available k3s cluster

✅ Load-balanced Kubernetes API via Hetzner

✅ Redundant control planes

✅ Scalable worker nodes

If one control plane node goes down, your cluster still works. Your load balancer ensures traffic is routed to the healthy node.

In production, you’d typically go with 3 or 5 control planes for full etcd quorum. But 2 is great for testing or small setups.


🧠 Final Thoughts

  • You’ve got a full Kubernetes cluster with minimal setup
  • k3s keeps it lightweight, perfect for edge, dev, or small-to-medium workloads
  • Hetzner makes it cost-effective and super easy to manage

Need more power? Just add more worker nodes — k3s scales well!


Enjoy your new high-availability k3s cluster on Hetzner! 🌍💡