When to graduate¶
This blueprint deliberately starts small. Moving up the ladder (Compose → Swarm →
k3s → full k8s) is only justified when specific things become true. Below are
concrete triggers. Don't graduate on vibes or resume-envy — graduate when a
trigger actually fires. (For learning k8s, ignore this file and use
kubernetes/ as a lab whenever you want; this file is about production
graduation.)
Rule of thumb¶
Add orchestration complexity only when the pain it removes is bigger than the pain it adds. At 4 home PCs, that bar is high.
Graduate Compose → Docker Swarm (stateless only) when ALL of these hold¶
- [ ] Addressing is rock-solid. Static DHCP reservations (or Tailscale) have been in place for weeks with zero name/IP-churn incidents. Swarm raft will flap otherwise.
- [ ] You have 2 or more genuinely stateless daemons whose uptime you care about enough to want automatic cross-node rescheduling.
- [ ] Manually bringing a daemon up on another host after a host failure has actually bitten you more than once.
- [ ] You are willing to babysit a raft quorum (an odd number of managers, reachable by stable names).
Still NOT in Swarm even after graduating: Postgres / any stateful service, and the GitHub-Actions runner.
Graduate Swarm/Compose → k3s when ALL of these hold¶
- [ ] You own at least one always-on Linux node with a static IP — e.g. a mini-PC (N100 / Raspberry Pi 5 / similar) that never sleeps. This is the big one: k3s wants a dependable Linux server for the control plane, not a Windows workstation in WSL2 that suspends.
- [ ] Ideally 3 such nodes (or 1 server + agents) so quorum survives one node down.
- [ ] The network is stable and every node can reach every other node (the isolated-subnet island is bridged or those nodes are excluded).
- [ ] You have a real need for k8s primitives — Ingress with cert management, HPA, operators, namespaced multi-tenancy — that Compose/Swarm genuinely can't serve.
- [ ] For any stateful workload you intend to run in-cluster, you have real storage: either an operator that replicates (e.g. CloudNativePG with multiple instances) or network/replicated storage (Longhorn, NFS, etc.). Local-path-only PVs pin data to one node and defeat rescheduling.
Graduate k3s → full Kubernetes (kubeadm) when ALL of these hold¶
- [ ] You need control k3s abstracts away (custom etcd topology, specific CNI, bespoke API-server flags, air-gapped/regulated setup).
- [ ] You have enough always-on Linux nodes to run a proper HA control plane (3+ control-plane nodes) plus workers.
- [ ] You (or the household) can carry the day-2 ops burden: etcd backups, cert rotation, version upgrades, CNI/storage lifecycle.
- [ ] The learning goal is specifically "operate raw upstream k8s" rather than "use the k8s API" (k3s already gives you the API).
For most homelabs this trigger never legitimately fires for production. If it's purely for the resume/skill, do it in an isolated VM lab.
Concrete example scenarios¶
-
"I bought an N100 mini-PC, put Debian on it, gave it a static IP, and it runs 24/7." → k3s single-server graduation trigger fired (control-plane home found). Move stateless daemons there; consider CloudNativePG for a real PG.
-
"I now have three always-on Linux mini-PCs on a stable wired subnet." → k3s HA is reasonable; even a small CloudNativePG cluster with replicas becomes a real (not lab) option.
-
"Still just my four Windows laptops/desktops on churny Wi-Fi." → stay on Compose + bespoke DR. No trigger has fired. Use
kubernetes/only as a lab.
De-graduation (going back down)¶
Graduating is reversible. If a Swarm/k3s experiment adds more 2am pages than it prevents, go back to Compose-in-Git. The Git-declared stacks make retreat cheap: the desired state is still committed, and the bespoke DR never depended on the cluster in the first place.