K3s Multi-Node: Adding a Second Machine to Your Cluster
From a single node to a real cluster in about ten minutes

A single-node K3s install is a wonderful thing. It’s Kubernetes that fits in a few hundred megabytes of RAM, it boots in seconds, and it lets you learn the whole ecosystem on one spare machine. But a cluster of one is a contradiction in terms. The day a sensible person finally gives in and orders a second mini PC — or rescues an old laptop from the cupboard — is the day K3s starts being genuinely useful. Pods can be rescheduled, you can drain a node to do maintenance, and a single dead disk stops being the end of the world.
Going from one node to two is delightfully undramatic. K3s was built for exactly this. Here’s how it actually works, and the handful of things that bite people.
1 The two roles: server and agent
K3s nodes come in two flavours. A server runs the control plane — the API server, scheduler, controller manager and the embedded datastore. An agent runs nothing but the kubelet and your workloads; it phones home to a server for instructions. Your existing single node is already a server. The simplest multi-node setup is to keep that server and bolt on an agent.
To join, an agent needs two things: the server’s URL and the cluster’s node token. The token lives on the server here:
sudo cat /var/lib/rancher/k3s/server/node-tokenCopy that value. While you’re on the server, note its LAN IP — say 192.168.1.10. That’s the address the agent will connect to, so it must be a stable one. Static IP or a DHCP reservation; do not skip this.
2 Joining the second machine
On the new machine, the install is a single command with two environment variables:
curl -sfL https://get.k3s.io | \
K3S_URL=https://192.168.1.10:6443 \
K3S_TOKEN="<the-node-token-you-copied>" \
sh -Because K3S_URL is set, the installer knows this is an agent, not a new server, and configures it to register against your existing control plane. Give it thirty seconds, then check from the server:
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION
node01 Ready control-plane,master 42d v1.28.5+k3s1
node02 Ready <none> 38s v1.28.5+k3s1That <none> under ROLES is correct — it’s a worker. You now have a real cluster. The scheduler will start placing new pods across both nodes automatically.
3 The networking gotchas nobody warns you about
This is where the ten-minute job becomes a forty-minute job if you’re unlucky. K3s’s default Flannel CNI uses VXLAN over UDP port 8472, the agent talks to the API on 6443, and the kubelet metrics flow back on 10250. If you run a firewall — and you should — open those between your nodes:
sudo ufw allow from 192.168.1.0/24 to any port 6443 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 10250 proto tcp
sudo ufw allow from 192.168.1.0/24 to any port 8472 proto udpSymptoms of getting this wrong are maddeningly specific: the node shows Ready, but pods on different nodes can’t reach each other, or logs and kubectl exec hang. Nine times out of ten it’s the VXLAN port silently dropped. The second classic trap is mismatched clocks — if the two machines’ times drift, TLS handshakes fail with confusing certificate errors. Run an NTP client on both and forget about it.
4 A note on storage
The moment you have two nodes, the default local-path storage provisioner becomes a quiet liability. It writes volumes to whichever node the pod first landed on. Reschedule that pod to the other node and the data simply isn’t there. For stateless workloads this is fine. For anything with a database, you now need either node affinity to pin it down or proper distributed storage. That’s a bigger conversation — for now, just know that two nodes plus local-path plus a wandering database is a data-loss trap waiting to spring.
5 Want high availability? Not like this
Adding an agent gives you workload resilience: lose the agent and pods reschedule onto the server. But the server is still a single point of failure — kill it and the whole control plane goes dark. True HA needs an odd number of servers (three is the magic number) with an embedded etcd quorum, started with the --cluster-init flag. That’s a worthwhile next step, but it’s a different job. For a homelab, one server and one or two agents is a perfectly honest setup.
6 The verdict
Adding a second node to K3s is one of the highest reward-to-effort tasks in self-hosting. For the cost of a cheap mini PC and ten minutes, you go from a fragile single box to something that survives a node reboot without taking your services down. Just respect the firewall ports and the clock, keep stateful workloads on a leash until you’ve sorted real storage, and don’t mistake a worker node for high availability. Get those right and your little cluster will quietly carry on when one machine doesn’t — which is the entire point of running Kubernetes in the first place.




