K3s Tutorial 2026: Lightweight Kubernetes for Raspberry Pi, Edge, and Home Lab
Kubernetes is the de-facto standard for container orchestration, but its full installation weighs in at hundreds of megabytes and requires significant resources — too much for a Raspberry Pi cluster, a cheap $5 VPS, or an IoT gateway running on the factory floor. That is the exact gap K3s was built to fill. Released by Rancher (now SUSE) in 2019, K3s packages a certified, production-grade Kubernetes distribution into a single binary under 100 MB. Its simplicity has driven roughly 200% year-over-year growth in edge and IoT deployments, and it has become the default choice for home labs and lightweight production clusters alike.
This tutorial covers everything you need to get a working K3s cluster: single-node server setup, adding worker nodes, deploying applications, configuring the built-in Traefik ingress controller, managing persistent storage, setting up a multi-node Raspberry Pi cluster, enabling high-availability mode, and a head-to-head comparison with competing distributions.
Prerequisites
- A Linux host (x86_64 or arm64) running Ubuntu 22.04 / Debian 12 or later, or a Raspberry Pi with a 64-bit OS.
sudoor root access.- Basic familiarity with Linux and containers. You do not need prior Kubernetes experience.
- For multi-node setups: two or more machines on the same network with SSH access between them.
1. What K3s Is and When to Use It
K3s is a fully conformant Kubernetes distribution, but it strips out everything that is not required for the core orchestration runtime:
| Removed from upstream K8s | What K3s uses instead |
|---|---|
| etcd (external datastore) | SQLite (default, single-node) or embedded etcd (HA mode) |
| Cloud-provider integrations (AWS, GCP, Azure) | Removed — add via Helm if needed |
| Alpha and deprecated features | Removed to reduce attack surface |
| In-tree storage drivers | Replaced by the Local Path Provisioner |
The result is a single binary (k3s) that includes the API server, scheduler, controller manager, kubelet, kube-proxy, containerd, and a CNI plugin (Flannel by default). No separate install steps for each component.
When K3s is the right choice:
- Raspberry Pi clusters — the arm64 binary runs natively on Pi 4 / Pi 5; full K8s is impractical.
- Home lab or development — spin up a cluster on a spare machine or a cheap VPS in under two minutes.
- Edge and IoT deployments — field devices with limited RAM (512 MB minimum) and intermittent connectivity.
- CI runners — ephemeral single-node clusters for integration tests cost pennies per run.
When to use full Kubernetes instead: large-scale multi-cloud environments where cloud-provider integrations, advanced scheduler plugins, or alpha features are mandatory. For everything else, K3s handles the load.
2. Install K3s Server (Single-Node)
The entire server installation is a single curl command:
curl -sfL https://get.k3s.io | sh -
The script detects your architecture, downloads the appropriate binary, installs a systemd service, and starts K3s. On a modern machine this takes about 30 seconds.
Verify the node is ready:
sudo kubectl get nodes
Expected output:
NAME STATUS ROLES AGE VERSION
my-server Ready control-plane,master 60s v1.29.4+k3s1
Copy the kubeconfig to your home directory so you can run kubectl without sudo:
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config
To use this config from a remote workstation, replace 127.0.0.1 with the server's actual IP address in the copied file.
Check that core system pods are running:
kubectl get pods -n kube-system
You should see pods for coredns, traefik, local-path-provisioner, and metrics-server all in Running state.
3. Add Worker Nodes
First, retrieve the node join token from the server:
sudo cat /var/lib/rancher/k3s/server/node-token
This prints a long string. Copy it, then run the following on each worker node — replace <server-ip> with the control plane's IP and <token> with the value you just copied:
curl -sfL https://get.k3s.io | \
K3S_URL=https://<server-ip>:6443 \
K3S_TOKEN=<token> \
sh -
Back on the server, confirm the worker has joined:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
my-server Ready control-plane,master 5m v1.29.4+k3s1
worker-01 Ready <none> 30s v1.29.4+k3s1
Repeat for as many workers as you need. K3s agents are also a single binary; the same k3s binary used on the server runs in agent mode when K3S_URL is set.
4. Deploy a Sample Application
Create a file named nginx-demo.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
spec:
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
Apply it:
kubectl apply -f nginx-demo.yaml
kubectl rollout status deployment/nginx-demo
Test with a quick port-forward:
kubectl port-forward svc/nginx-demo 8080:80 &
curl http://localhost:8080
You should see the nginx welcome page HTML. Kill the port-forward with kill %1 when done.
5. Traefik Ingress (Built-In)
K3s ships Traefik 2.x as its default ingress controller — it is already running. You only need to create an Ingress resource to expose a service at a hostname.
Create nginx-ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-demo
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: nginx.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-demo
port:
number: 80
kubectl apply -f nginx-ingress.yaml
Point nginx.example.com to your server's IP via DNS (or /etc/hosts for local testing), then visit the hostname in a browser.
TLS with cert-manager and Let's Encrypt — install cert-manager first:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
Create a ClusterIssuer for Let's Encrypt, then add these annotations to your Ingress:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- nginx.example.com
secretName: nginx-demo-tls
cert-manager will automatically obtain and renew the certificate. Traefik picks up the secret and serves HTTPS without any further configuration.
6. Persistent Storage with Local Path Provisioner
K3s bundles the Rancher Local Path Provisioner, which dynamically provisions hostPath volumes. The default StorageClass is named local-path.
Create a PersistentVolumeClaim:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 1Gi
Mount it in a pod:
volumes:
- name: data
persistentVolumeClaim:
claimName: demo-pvc
containers:
- name: app
image: busybox
volumeMounts:
- mountPath: /data
name: data
The provisioner creates the directory under /var/lib/rancher/k3s/storage/ on the node that schedules the pod. For production multi-node clusters, consider Longhorn (also from SUSE/Rancher) which provides replicated block storage and is installable via Helm with full K3s support.
7. K3s on Raspberry Pi
Requirements: - Raspberry Pi 4 or 5 (2 GB RAM minimum; 4 GB recommended for control plane) - Raspberry Pi OS Lite 64-bit — K3s requires a 64-bit kernel. The 32-bit OS is not supported. - A Class 10 microSD or, preferably, a USB SSD for the control plane node.
Enable cgroup memory — without this, the kubelet will fail to start. Edit /boot/firmware/cmdline.txt (Raspberry Pi OS Bookworm) or /boot/cmdline.txt (older) and append the following to the existing single line (do not add a newline):
cgroup_memory=1 cgroup_enable=memory
Reboot, then install K3s exactly as described in section 2. The install script detects arm64 automatically.
Multi-node Pi cluster: use one Pi as the server (control plane) and the remaining Pis as agents. Run the agent join command from section 3 on each worker Pi. A three-node Pi 5 cluster with 8 GB RAM per node is powerful enough to run a complete production-like environment including monitoring (Prometheus + Grafana), logging, and several microservices.
8. High-Availability K3s (Embedded etcd)
For production workloads, a single control plane is a single point of failure. K3s supports HA with an embedded etcd cluster using three or more server nodes.
Bootstrap the first server with the --cluster-init flag:
curl -sfL https://get.k3s.io | K3S_TOKEN=<shared-token> sh -s - \
--cluster-init
Join the second and third server nodes (not agents — these are also control-plane members):
curl -sfL https://get.k3s.io | K3S_TOKEN=<shared-token> sh -s - \
--server https://<first-server-ip>:6443
All three servers participate in etcd consensus. You can lose one server and the cluster keeps running. Place a load balancer (HAProxy, nginx, or a cloud NLB) in front of the three API server addresses and point your kubeconfig at the load balancer's IP.
For worker nodes in an HA setup, the join command is identical to section 3 — point K3S_URL at the load balancer address.
9. K3s vs K8s vs MicroK8s vs k0s
| Feature | K3s | Full Kubernetes (kubeadm) | MicroK8s | k0s |
|---|---|---|---|---|
| Binary size | < 100 MB | ~700 MB+ (multiple binaries) | Snap package (~200 MB) | ~150 MB |
| Min. RAM | 512 MB | 2 GB | 540 MB | 512 MB |
| Default datastore | SQLite | etcd | etcd (dqlite for HA) | SQLite / etcd |
| Arm64 support | Yes (official) | Yes | Yes | Yes |
| Built-in ingress | Traefik | None | NGINX (addon) | None |
| Built-in storage | Local Path | None | Hostpath (addon) | None |
| HA mode | Embedded etcd | External or stacked etcd | dqlite / etcd | Embedded / external etcd |
| Best for | Edge, Pi, home lab, dev | Large-scale production | Ubuntu/Snap ecosystems | Air-gapped, immutable OS |
| CNCF certified | Yes | Yes | Yes | Yes |
TL;DR: K3s wins on simplicity and resource efficiency. Full Kubernetes wins for large teams that need advanced scheduler extensions or cloud-provider integrations. MicroK8s is a strong option on Ubuntu desktops. k0s is worth evaluating if you need an immutable or air-gapped setup.
10. FAQ
Q: Can I use kubectl from my laptop to manage a K3s cluster? Yes. Copy /etc/rancher/k3s/k3s.yaml from the server to ~/.kube/config on your laptop, change 127.0.0.1 to the server's public IP, and ensure port 6443 is reachable. Any standard kubectl binary works.
Q: Is K3s production-ready? Yes. K3s is CNCF-certified Kubernetes and used in production by companies like SUSE, various telcos, and countless edge deployments. The embedded SQLite datastore is fine for small single-node clusters; use embedded etcd (section 8) for anything that needs high availability.
Q: How do I upgrade K3s? Re-run the install script — it will upgrade in place:
curl -sfL https://get.k3s.io | sh -
For agents, run the same command with the K3S_URL and K3S_TOKEN environment variables set.
Q: Can K3s run on Docker instead of containerd? Yes, though it is not recommended. Pass --docker to the install script and ensure Docker is installed. The default containerd runtime has better integration and performance.
Q: How do I uninstall K3s?
# Server
/usr/local/bin/k3s-uninstall.sh
# Agent
/usr/local/bin/k3s-agent-uninstall.sh
Q: Can I replace Traefik with NGINX ingress? Yes. Disable Traefik during install and install NGINX ingress via Helm:
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik" sh -
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
K3s removes the operational complexity that makes Kubernetes feel unapproachable for small teams and resource-constrained environments, while preserving full API compatibility. A working cluster is one curl command away, Traefik and storage come pre-configured, and the same YAML you write for K3s runs unchanged on any other Kubernetes distribution. For home labs, edge deployments, and anyone taking their first serious steps with Kubernetes, K3s is the right starting point in 2026.