Skip to main content

High-Availability k3s Cluster Guide with k3sup and kube-vip

Introduction 

What is k3s?

k3s is a lightweight, fully compliant Kubernetes distribution designed for low-resource environments. It simplifies installation and management, making it perfect for development, testing, or small clusters.

Why k3sup?

k3sup (“ketchup”) is a simple CLI tool that installs k3s over SSH on any remote server. It handles the complexities of bootstrap, joining nodes, and copying kubeconfig automatically—ideal for lab setups or multi-node deployments.

Why kube-vip?

k3s HA clusters require a load balancer or a virtual IP to ensure the control plane remains reachable if a master node fails. kube-vip provides a lightweight, Kubernetes-native virtual IP that floats between master nodes, maintaining high availability without complex external load balancers.

Prerequisites

  • Nodes: At least 3 nodes (in this guide, we'll have all 3 nodes as the control plane and worker)

  • OS: We are going to use Debian but most Linux distro would be fine

  • Network: All nodes must communicate over a LAN

  • Tools installed locally: ssh access to all nodes, curl or wget

  • Architecture note: Can mix ARM and x86_64 nodes. In my case, I currently have two Raspberry Pis and an old refurb ThinkPad X240. If you are using a Raspberry Pi, it's important that you edit cmdline.txt in /boot/firmware, then add:
    cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1. Those kernel parameters are related to Linux control groups (cgroups), which Kubernetes (and therefore K3s) relies on to manage resource allocation for containers.

Installation 

Now let's get our hands dirty. With k3sup making our life easier, we can just configure everything from our local computer's command line. I would recommend you to spin up a Debian WSL if you are on Windows so you can have a Linux environment command line.

Install k3sup:

curl -sLS https://get.k3sup.dev | sh
sudo install k3sup /usr/local/bin/

k3sup uses ssh to connect to the server and agent nodes so we need to copy our public key. Generate a public key by executing:

ssh-keygen

Copy the generated ssh keys to your nodes:

ssh-copy-id sysad@192.168.100.201
ssh-copy-id sysad@192.168.100.202
ssh-copy-id sysad@192.168.100.203

Note: make sure your linux user for every node is not required to re-enter password. Add it to visudo by executing sudo visudo and adding the line: [your-linux-user] ALL=(ALL) NOPASSWD:ALL 

Install the kubectl (so you can run commands off your non k3s environment)

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"


#make it executable 
chmod +x kubectl

#Move it to your local path
sudo mv kubectl /usr/local/bin/kubectl  

We are now ready to install and configure your first master node: 

k3sup install \
  --ip 192.168.100.202 \ #this will be the IP of your node
  --user sysad \
  --cluster \ 
  --ssh-key ~/.ssh/id_ed25519 \ #change this depending on where your ssh key is in your local machine
  --tls-san 192.168.100.210 \ #this will be your control plane virtual IP
  --k3s-extra-args "--disable servicelb --disable traefik"

The command below will allow you to use the kubectl command: export KUBECONFIG=$(pwd)/kubeconfig 

Before we join the rest to the cluster, it's nice to have kube-vip installed and configured first.

Apply RBAC permissionspermissions:

kubectl apply -f https://kube-vip.io/manifests/rbac.yaml

alias kube-vip="sudo ctr run --rm --net-host docker.io/plndr/kube-vip:latest vip /kube-vip" 

Or if you have docker engine in your machine:

alias kube-vip="sudo docker run --network host --rm ghcr.io/kube-vip/kube-vip:main" 

Apply manifest:

kube-vip manifest daemonset \
    --interface eth0 \
    --address 192.168.100.210 \ #this will be your control plane virtual IP
    --inCluster \
    --controlplane \
    --services \
    --arp \
    --leaderElection |  ssh sysad@192.168.100.202 "sudo tee /var/lib/rancher/k3s/server/manifests/kube-vip.yaml"

Now we can join the rest of the nodes:

# We can now use the virtual IP we set earlier as the server ip :)
k3sup join --ip 192.168.100.201 --user sysad --server --ssh-key ~/.ssh/id_ed25519 --server-ip 192.168.100.210 --server-user sysad
k3sup join --ip 192.168.100.203 --user sysad --server --ssh-key ~/.ssh/id_ed25519 --server-ip 192.168.100.210 --server-user sysad

That's it! We'll soon test it's HA and deploy our first app. Stay tuned!