Blog
k3s on One Node: A Lightweight Kubernetes Lab
To kick off 2026 I set myself a small goal: systematically rebuild my Kubernetes muscle memory — without burning a multi-node control plane at home. I picked single-node k3s: easy install, the common components bundled in, and friendlier resource usage than a full upstream cluster. What follows is a four-week learning log, organized by week rather than a retread of the official docs' table of contents.
Day zero: install it, confirm it's alive
curl -sfL https://get.k3s.io | sh -
sudo kubectl get nodes
sudo kubectl get pods -A
Once the node is Ready, you'll see traefik, local-path provisioner, and similar components — enough to practice on. The kubeconfig lives at /etc/rancher/k3s/k3s.yaml; copy it into your own home directory and fix the permissions so you're not glued to sudo all day. Day zero has exactly one job: get nodes works and you can see the Pods in kube-system. Don't rush to install a Service Mesh. If the installer pulled in components you don't need, you can switch them off later; the day-zero principle is "first have a cluster that answers", not "make it production-grade in one shot".
Week one: Deployments, Services, probes
This week's goal is to make the "desired replica count" and the real Pods line up. Write a Deployment, change the image tag, and watch whether the rolling update honors maxUnavailable; deliberately typo the image and see how the events complain. Use a ClusterIP Service to curl through inside the cluster first, then understand why NodePort shouldn't be casually left naked to the public internet.
Probes: I got the lesson of a liveness check killing processes for nothing once. I wrote the check path wrong, the Pod kept restarting, and the app logs read like a haunted house. Readiness should keep traffic away until the Pod is genuinely ready — that's even more visible behind an Ingress. Weekend review in one sentence: declarative doesn't mean "write it and forget it"; it means "write it and know how to read status and events".
Week two: Ingress and certificates
Where entry traffic comes in and where TLS terminates is this week's theme. Use the bundled traefik or swap in an Ingress controller you know better — either is fine; the key is that path rules and backend Service names aren't written loose. For certificates, use an internal CA or a Let's Encrypt experimental domain and walk the request-and-renew flow end to end; a green lock on home-lab HTTPS is about practicing the process, not chasing public-internet perfection.
Pitfalls I hit: annotations written on the wrong resource, a Service port vs container port mismatch by one layer, DNS still pointing at the old IP. Break "the browser won't open" into DNS → ingress → backend Pod → the app itself and check layer by layer — more useful than restarting the cluster. This week I forced myself to debug two failures each with describe and logs, not by clicking through a web UI. The feel for it comes from the terminal, not dashboard screenshots.
Week three: PVCs, PVs, and state
Once stateless Deployments feel comfortable, bring in PVCs. local-path is fine for experiments: create, mount, delete the Pod, and confirm the data survives. The takeaway: Pods are throwaway, but a volume's lifecycle is a separate thing. Before deleting a namespace, check the reclaim policy so you don't collect a data volume you still wanted.
One important conclusion goes into the log early: local-path is not for putting the "only copy" of a production database on a single node. Important data still lands on NAS volumes or in a database outside the cluster. A single-node k3s has no multi-replica control plane and no cross-node scheduling fallback — storage reality bites harder than YAML syntax.
Week four: Helm and the "don't put it in" list
Use Helm to install a dashboard or a common chart, and force yourself to read the values instead of blindly copying a whole page of config. Change values, upgrade the release, and see whether you can roll back to an old version. When labels and annotations start multiplying, find resources with custom columns and label selectors on kubectl get instead of getting lost in the web UI.
Deliberately not touched: Service Mesh, a full suite of Operators, and migrating every Compose service into the cluster en masse. Things that don't belong go on the list at the end of the weekly log — shoving an unbacked-up production database in there; NodePort naked without understanding the network model; treating single-node k3s as a high-availability production control plane. Keep the mental models for a learning environment and a production environment separate.
After four weeks: how it coexists with Compose
My lab split: Compose keeps running the steady-state self-hosted services (blog, photo library, monitoring); k3s runs experimental and learning workloads. Don't go "all in on K8s" just to go all in — operational complexity is a real cost. At least three times in those four weeks I was tempted to migrate monitoring into the cluster too, and I held off every time. The learning goal is muscle memory, not creating two overlapping alert storms.
A single-node k3s won't teach you everything about large-scale operations, but YAML feel, debugging order, and the boundaries of storage and ingress are all trainable in one break. Even single-node needs limits: give the experimental namespace a ResourceQuota so one wrong replica count doesn't peg the whole machine's CPU and make even SSH crawl. That's where this log ends: once you can walk, think about whether you want a second node — that's next season's story. This season's acceptance criterion is one thing only: two weeks after closing the laptop, you can still write a Deployment, Service, Ingress, and PVC from scratch and debug one failure end to end.
Three things I kept after the four weeks
When the lab wrapped up, there was no production photo album running in the cluster. What stayed was:
- A
kustomizationexample I wrote myself, to copy the structure from later - Respect for "storage classes and permissions" — worth more than memorizing kubectl subcommands
- Clear boundaries: Compose keeps managing the steady-state services; k3s is only for learning and the occasional operator experiment
Some people move the whole family's services into k3s after four weeks, and it runs. My split is an energy budget: part of the fun of a home lab is keeping the complexity curve under control, not proving yourself on a résumé-grade architecture. Single-node k3s did its teaching job, and that's enough.