K3s is Kubernetes, but it’s also not.
Let’s see K3s in action. Imagine you’re setting up a small cluster for some home lab projects or a development environment. You’ve got a couple of Raspberry Pis, or maybe a couple of old laptops.
First, you SSH into your first node. You’ve already got a basic Linux distro on it, like Ubuntu or Debian.
curl -sfL https://get.k3s.io | sh -
That’s it. Seriously. If you wanted to add another node later, you’d grab the IP address of your first node (let’s say it’s 192.168.1.100) and then on the new node, run:
curl -sfL https://get.k3s.io | K3S_URL=https://192.168.1.100:6443 K3S_TOKEN=YOUR_NODE_TOKEN sh -
You’d get YOUR_NODE_TOKEN from the file /var/lib/rancher/k3s/server/node-token on your first node.
Now, let’s look at what’s not different. You can use kubectl just like you would with any other Kubernetes cluster.
kubectl get nodes
Output might look like this:
NAME STATUS ROLES AGE VERSION
k3s-agent1 Ready <none> 5m v1.28.5+k3s1
k3s-server Ready server (control-plane) 10m v1.28.5+k3s1
You can deploy applications using standard Kubernetes manifests. Here’s a simple Nginx deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
And apply it:
kubectl apply -f nginx-deployment.yaml
This solves the problem of needing a full-blown, resource-hungry Kubernetes distribution for smaller, edge, or development use cases. Traditional Kubernetes distributions can be overkill, requiring significant CPU and RAM, complex installation, and multiple components to manage. K3s strips away the complexity and resource requirements while retaining the core Kubernetes API and functionality. It bundles essential components like containerd, CoreDNS, Traefik (as an ingress controller), and local-path-provisioner into a single binary.
Internally, K3s achieves its lightweight nature through several key changes:
- Embedded etcd or SQLite: Instead of relying on an external etcd cluster (which is the default for most Kubernetes setups), K3s uses SQLite as its default data store for single-node clusters. For multi-node clusters, it can still use etcd, but it also offers a bundled etcd operational mode. This dramatically simplifies state management.
- Single Binary: The entire K3s control plane and agent are packaged into a single binary. This makes installation and upgrades incredibly straightforward.
- Removed Legacy/Alpha Features: K3s removes features that are considered legacy, alpha, or beta, and features not typically needed at the edge or in smaller deployments. This reduces the attack surface and resource footprint.
- Simplified Networking: It uses Flannel by default for CNI (Container Network Interface) and Traefik for ingress, which are lightweight and easy to configure.
The most surprising thing about K3s is how it handles its API server and controller manager. Instead of running these as separate processes managed by a systemd unit or similar, K3s embeds them directly within the k3s server binary. When you start k3s server, it’s not just launching a Kubernetes API server; it is the API server, the controller manager, and the scheduler, all within a single process. This is a fundamental departure from the standard Kubernetes architecture and is a primary reason for its reduced overhead and simpler deployment.
The next logical step is understanding how to manage storage in K3s for persistent applications.