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! 🌍💡