k3sup vs kubeadm for Homelab Clusters
Two philosophies show up at your doorstep: kubeadm, the official upstream bootstrapper that hands you a box of parts and a wrench, and k3sup, a one-shot SSH installer that leans on k3s and says “here’s your cluster, go play.” Both build Kubernetes clusters — but they have very different attitudes about how much work you want to do at 2 AM.
Take a breath. Close those tabs. This isn’t about religious wars. It’s about decisions that affect your nights, your backups, and whether you enjoy debugging CNI CRDs or sipping coffee while your cluster boots itself.
The kubeadm philosophy (what it actually does)
kubeadm is upstream Kubernetes’ official bootstrap tool. It’s honest and blunt: it sets up the control plane components (kube-apiserver, controller-manager, scheduler), generates certs, and gives you join tokens. After that, it’s your job to wire the rest: pick a CNI, choose a storage class, add ingress, metrics, monitoring, and any other opinions you want in your cluster.
Think of kubeadm as buying an engine, gearbox, and chassis. It tells you how to bolt those bits together properly, but it doesn’t install the seats, air conditioning, or infotainment system. That freedom is powerful — and also the reason your first kubeadm cluster inevitably ends with someone on Slack complaining about broken DNS and a flurry of kubectl describe commands.
Core behaviors:
- init/join bootstrap flow using tokens and CA hashes
- minimal-by-design: no networking, no storage provisioner, no ingress supplied
- great for learning, testing upstream behavior, and production where you want exact control
The k3sup philosophy (what it actually does)
k3sup is a tiny remote bootstrapper that uses SSH to install k3s, Rancher’s lightweight Kubernetes distribution. k3sup itself is not Kubernetes — it’s a convenience tool that executes k3s installers over SSH and copies kubeconfig back to your laptop.
k3s is opinionated: it bundles a CNI (Flannel by default), a local-path provisioner for storage, and a default ingress (Traefik in many releases). k3sup leans on that opinionated stack and makes the whole thing a one-liner remote install.
Think of k3sup as buying a pre-built scooter and having a friend deliver it to your driveway and hand you the keys. It’s fast, predictable, and gets you moving.
Practical kubeadm example: init, token, join
Kubeadm flow is explicit. On your control plane node:
sudo kubeadm init --apiserver-advertise-address=192.168.1.10 \ --pod-network-cidr=10.244.0.0/16That outputs the join command the worker will use, or you can create a token explicitly:
# prints a ready-to-run kubeadm join command with token and discovery hashsudo kubeadm token create --print-join-commandOn a worker node, run the printed command, e.g.:
sudo kubeadm join 192.168.1.10:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXFor a control-plane join (HA), generate a cert key and use —control-plane:
sudo kubeadm init --upload-certs --apiserver-advertise-address=192.168.1.10
# On join nodesudo kubeadm join 192.168.1.10:6443 --token <tok> \ --discovery-token-ca-cert-hash sha256:<hash> \ --control-plane --certificate-key <cert-key>That’s kubeadm: manual, explicit, and fine-grained.
Practical k3sup example: install, join, server/agent model
k3sup’s magic is simple: it SSHes into the target, runs the k3s installer, and wires kubeconfig back to your laptop. Example server install:
k3sup install --ip 192.168.1.20 --user pi --local --k3s-version v1.30.4+k3s1Add an agent (worker):
k3sup join --ip 192.168.1.21 --server-ip 192.168.1.20 --user piMake a second server (HA) by joining with the —server flag (k3s will handle control plane replication):
k3sup join --ip 192.168.1.22 --server --server-ip 192.168.1.20 --user pik3sup is ideal when you want a simple, repeatable path to a working cluster without hand-holding low-level details.
HA control plane: kubeadm vs k3sup
Both support HA control planes, but the experience and mechanics differ substantially.
kubeadm HA is explicit and hands-on:
- You run kubeadm init on a primary control plane with —upload-certs, which generates a certificate key for joining other control planes
- On additional control-plane nodes use kubeadm join —control-plane plus —certificate-key
- You manually manage a load balancer or virtual IP for kube-apiserver endpoints (keepalived/HAProxy/MetalLB) so clients talk to a stable address
- You choose your datastore: embedded etcd (simple, single-quorum) or external etcd (safer but more ops)
- You manage certificates across the control plane, including etcd peer certs if running separate etcd
k3sup + k3s HA is designed for simplicity:
- k3s has built-in options for embedded datastore or external DB (etcd/consul/postgres), handled transparently
- k3sup’s —server join uses k3s mechanisms to replicate the control plane and handle datastore membership automatically
- You’ll likely still want a small load balancer or DNS record, but for homelabs k3s often works fine with embedded datastore on 3 servers
- Less certificate gymnastics — k3s handles internal PKI
Bottom line: kubeadm HA gives you complete control and therefore more surfaces to manage; k3sup HA gives you a consistent, smaller-surface workflow that gets redundancy working quickly. For a 3-node homelab, k3sup is the obvious choice. For production at scale, kubeadm’s transparency and fine-grained control is worth the ops complexity.
What gets shipped: vanilla k8s vs k3s defaults
Vanilla kubeadm intentionally ships minimal Kubernetes binaries and certs. It’s a blank canvas: you get the control plane, kubelet, kubectl, and nothing else. You must pick and install:
- CNI (Calico/Weave/Flannel/Curiefense) — networking and pod-to-pod policy. Without it, pods can’t talk to each other.
- Storage provisioner (local-path, rook, longhorn) — dynamic PVCs. Without it, persistent data is manual.
- Ingress (nginx/traefik/Contour) — routing external traffic into services. Essential for web workloads.
- Metrics-server, dashboard, logging, monitoring (Prometheus/Grafana) — observability is optional but you’ll want it.
k3s (thus k3sup installs) is opinionated by default. Typical defaults: Flannel for CNI, a local-path provisioner for PVCs, Traefik for ingress (depending on k3s version), and a small DNS implementation. Out of the box, you have a usable cluster. Pods get IPs, storage works, and external traffic routes to services.
Opinionated defaults are a feature for homelabs: you get a working environment fast without homework. The trade-off is you might outgrow the defaults later (swap Flannel for Calico, replace Traefik with Nginx, add a real storage backend). But early on, you’re not frantically installing manifests at 2 AM just to get DNS working.
Think of it this way: kubeadm is a blank slate that teaches you every decision; k3s is a sensible starting point that you customize as needed.
The CNI choice story — “I picked Calico” pain
This one is a rite of passage:
- You install kubeadm
- You choose Calico for network policy and advanced features
- Pods fail to get IPs, DNS times out, and you spend an evening debugging CNI manifests and CRDs
Calico is powerful, but it has more moving parts than Flannel. It needs BGP speakers, custom resource definitions for network policies, and careful IP pool management. For homelabs, that complexity costs time and sleep. k3s+Flannel trades raw power for reliability. Flannel is simple: overlay VXLAN, minimal dependencies, works out of the box.
If you want Calico for learning or because your homelab demands network policies across pods, fine — but expect additional troubleshooting, more research, and extra moving parts during upgrades. You’ll be reading Calico docs at 2 AM instead of enjoying your services.
If your goal is “learn Kubernetes internals and network policy details,” kubeadm + Calico is educational. If the goal is “run services at home with minimal babysitting,” k3sup + k3s + Flannel is the pragmatic choice. There’s no prize for the harder path if a simpler one works.
Storage class reality — who wires PVCs?
Kubeadm: no storage provisioner by default. That means new PersistentVolumeClaims sit in Pending until you install a provisioner like Local Path Provisioner, Longhorn, or Rook. You have to decide where the volumes live: local SSDs, NFS, or a networked solution.
k3s: ships with local-path-provisioner (or something similar) so PVCs work out of the box for many home setups. It’s not a cloud-grade solution, but for dev/test and even a small homelab, it’s a practical default.
If your NAS is central to your homelab, you’ll probably swap in Longhorn or Rook later. Again: kubeadm gives you options; k3s gives you defaults that are less scary at midnight.
Upgrades: kubeadm upgrade vs re-run k3s/k3sup
kubeadm upgrades are deliberate and multi-step:
- Upgrade kubeadm package on control plane
- Run sudo kubeadm upgrade apply vX.Y.Z (which checks for incompatibilities and plans the upgrade)
- Upgrade kubelet/kubectl packages on other nodes and restart kubelet daemon
- Verify cluster health before declaring success
This process is well-documented, suitable for production, and gives you fine-grained control over when each component upgrades. But it also means more manual steps, more reading release notes, and more surfaces for things to go wrong.
k3s upgrades are designed for simplicity. With k3sup you re-run the install/join commands with a new —k3s-version to swap binaries. It’s idempotent: the same command works for install, upgrade, or re-run. Simpler and messier at the same time: great for homelabs, but less surgical than kubeadm’s controlled path. k3s is also lighter on the system, so a full re-install on a Raspberry Pi is faster.
A kubeadm upgrade example:
# on control planesudo apt-get update && sudo apt-get install -y kubeadm=1.27.3-00sudo kubeadm upgrade plan # Check what’s going to changesudo kubeadm upgrade apply v1.27.3# then upgrade kubelet/kubectl on nodes and restart kubeletsudo systemctl restart kubeletAnd k3s upgrade (via k3sup):
# Re-run install with new versionk3sup install --ip 192.168.1.20 --user pi --k3s-version v1.27.3+k3s1# Re-run join for agents if neededk3sup join --ip 192.168.1.21 --server-ip 192.168.1.20 --user pi --k3s-version v1.27.3+k3s1Same command, new version, cluster updated. Less control, less ceremony, more uptime.
Tear-down: reset vs uninstall
When it’s time to tear down a cluster:
kubeadm:
sudo kubeadm reset -fsudo apt-get purge -y kubeadm kubectl kubeletsudo rm -rf /etc/cni /var/lib/cni /var/lib/kubelet /etc/kubernetesk3s:
sudo /usr/local/bin/k3s-uninstall.sh# on agentssudo /usr/local/bin/k3s-agent-uninstall.shk3s tends to be cleaner to remove because fewer external components were manually installed.
When kubeadm IS the right tool
Use kubeadm if you actually need the guarantees and tooling of upstream Kubernetes:
- You’re studying for the CKA or learning Kubernetes internals. kubeadm forces you to know how the control plane and kubelet interact.
- You need upstream conformance testing and reproducible behavior identical to cloud-managed clusters.
- You’re running production clusters at larger scale and need fine-grained control over networking (Calico), storage (Rook/Longhorn with dedicated disks), and high-availability etcd setups.
- You need to integrate with enterprise systems that expect certain CNI plugins, CSI drivers, or admission controllers.
Basically: if you want power and are comfortable with complexity, kubeadm is the right choice.
Conclusion — which wins for your homelab?
k3sup (with k3s) wins for almost every homelab use-case. It’s fast, repeatable, and keeps the nightly gremlins away. For running a few services — Home Assistant, a photo server, a CI runner, or messing with LLMs — k3sup gives you a working Kubernetes cluster in minutes. You’re trading a little upstream purism for a lot of convenience.
kubeadm is excellent if your goal is education, strict upstream parity, or running a production-sized cluster where every component must be intentionally chosen and operated. But if you’re the sort of person who’s already apologizing to their 2 AM self, k3sup will save you hours.
There’s no prize for running upstream Kubernetes at home if a Compose file or k3sup cluster does the job better. Your 2 AM self will appreciate it.
Recommendations quick-hit:
- Want a cluster in 10 minutes? Use k3sup + k3s.
- Want to learn Kubernetes internals or prepare for CKA? Use kubeadm.
- Need HA and don’t mind ops? Either works — kubeadm for control, k3sup for speed.
Happy clustering. Don’t forget to back up your etcd or your k3s datastore before tinkering — and yes, label your nodes so you don’t accidentally schedule your NAS as a control-plane.