2.3 k3s: Lightweight Kubernetes for Edge and Raspberry Pi
Alright, let’s talk about k3s. You’ve heard the siren song of Kubernetes, but the thought of deploying the full-blown, resource-hogging behemoth on your Raspberry Pi cluster or your edge device makes you break out in a cold sweat. Good. That’s a sane reaction. The full kubeadm setup is like using a industrial wrecking ball to hang a picture frame. Enter k3s, from the fine folks at Rancher (now SUSE). It’s Kubernetes, but with the fat trimmed off. And by fat, I mean they ripped out a stunning amount of legacy cruft and cloud-specific drivers you’d never need on a Pi. It’s so lean it almost feels like cheating.
Why k3s is an Absolute Game-Changer for the Edge
The magic of k3s isn’t just that it’s small. It’s that it’s complete and simple. It’s a single binary that contains every major component you need. The kubelet, the kube-proxy, the container runtime (they swapped out Docker for the more lightweight containerd), even the control plane components (api-server, scheduler, etcd) are all compiled into one tidy, sub-40MB package. They even replaced etcd with the much more resource-friendly SQLite by default, which is a design choice so sensible it makes you wonder why the mainstream K8s doesn’t offer it out of the box. This architectural simplicity means you can have a fully functional, certified Kubernetes cluster up and running with one command. One. Let that sink in after your last multi-hour kubeadm fight.
Installation: Stupidly Simple, Deceptively Powerful
Here’s the part that will blow your mind. You install the server (which will act as both a control plane and a worker node by default) like this:
curl -sfL https://get.k3s.io | sh -
Yes, that’s it. No, really. Go try it. I’ll wait.
…See? Told you. Behind that simple script, a few key things happen: the k3s binary is downloaded, a systemd service is created and started, and a kubeconfig file is written to /etc/rancher/k3s/k3s.yaml. Now, to use kubectl, you need to do two things. First, copy that config to your user directory and set the permissions (Kubernetes will throw a fit if the config is world-readable, which is a good security practice, honestly).
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
Second, because the kubeconfig file points to localhost, you’ll need to edit it if you want to access the cluster from another machine. Change the server URL from https://127.0.0.1:6443 to https://<your-server-ip>:6443.
To add a worker node to this cluster, you just install k3s on another machine and point it at your server. You’ll need the token from the server node, found at /var/lib/rancher/k3s/server/node-token. Then, on the worker:
curl -sfL https://get.k3s.io | K3S_URL=https://<your-server-ip>:6443 K3S_TOKEN=<your-node-token> sh -
Boom. You now have a multi-node cluster. It feels like magic, but it’s just good engineering.
The One “Gotcha”: The Embedded Components and How to Tame Them
Remember when I said it was a single binary? This is k3s’s greatest strength and its main quirk. You don’t systemctl start kube-apiserver. You just start k3s. This means if you need to configure a specific component, you do it through k3s’s flags and config file, not by modifying individual component manifests in /etc/kubernetes/manifests.
For most use cases, the defaults are perfect. But when you need to tweak, you have two main tools. First, you can set environment variables for the service. For example, to pass flags to the kubelet, you’d use the K3S_KUBELET_ARGS environment variable. The best way to do this persistently is by editing the systemd service drop-in file:
sudo mkdir -p /etc/systemd/system/k3s.service.d/
sudo tee /etc/systemd/system/k3s.service.d/override.conf > /dev/null << EOF
[Service]
Environment="K3S_KUBELET_ARGS=--max-pods=150"
EOF
sudo systemctl daemon-reload
sudo systemctl restart k3s
For more complex configurations, you can use a proper config file at /etc/rancher/k3s/config.yaml. This is where you’d set things like the datastore endpoint (if you’re using an external SQL database or etcd instead of SQLite), or disable the built-in load balancer (servicelb) if you’re deploying MetalLB.
# /etc/rancher/k3s/config.yaml
write-kubeconfig-mode: "0644"
node-label:
- "size=small"
disable:
- servicelb
- traefik
Ah yes, Traefik. k3s ships with Traefik as its default ingress controller. It’s a solid choice, but if you’re a die-hard nginx-ingress fan, your first move will be to disable it and install your own. This is a perfectly valid and common first step.
Best Practices: Don’t Just Wing It
Even though it’s “lightweight,” this is still Kubernetes. Treat it with respect.
- Secure Your Token: That node token is the keys to the kingdom. Protect it like any other secret.
- Use an External Datastore for HA: For a single-node Pi, SQLite is brilliant. For a true production-grade, multi-master high-availability setup, you must use an external datastore like etcd or MySQL/PostgreSQL. The k3s docs are excellent on this front.
- Embrace the Klipper Load Balancer: The built-in
servicelb(formerly called Klipper LB) is a clever solution for edge environments where you can’t easily deploy a traditional metalLB. It simply assigns a node port to your LoadBalancer service and assigns the node’s IP to it. It’s absurdly simple and works shockingly well for homelabs and edge scenarios. Don’t fight it until you have a specific reason to. - Persistent Storage: This is the trickiest part on edge devices. Your Pis probably don’t have a fancy CSI driver. Your best bet is to use the
localvolume provisioner or a simple NFS setup. Manage your expectations accordingly.
k3s isn’t a toy. It’s a masterclass in pragmatic engineering that recognizes that the formality of the cloud doesn’t always fit the constrained reality of the edge. It lets you stop fighting the tool and start building what you actually wanted to build. Now go deploy something.