k3s is a fully conformant Kubernetes distribution packaged as a single binary. It bundles containerd, a network plugin (Flannel), an ingress controller (Traefik), a local storage provisioner and a service load balancer — so a working cluster takes one command. It runs well on a single small VPS, which makes it ideal for learning, small production workloads, edge sites and homelabs.
Requirements
- A Linux VPS on KVM (Ubuntu, Debian, AlmaLinux, Rocky…).
- 1 vCPU and 1 GB RAM minimum for a server node; 2 GB+ is comfortable once you deploy apps.
- Open ports between nodes: 6443/tcp (API), 8472/udp (Flannel VXLAN), 10250/tcp (kubelet). Publicly: only 80/443 for your apps.
1. Install a single-node cluster
curl -sfL https://get.k3s.io | sh -
The script installs k3s as a systemd service. Check:
sudo k3s kubectl get nodes
sudo systemctl status k3s --no-pager
k3s writes the kubeconfig to /etc/rancher/k3s/k3s.yaml. To use kubectl as a regular user:
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER: ~/.kube/config
kubectl get pods -A
Pin a version or channel for predictability: curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=stable sh -.
2. Add worker nodes (optional)
On the server, get the join token:
sudo cat /var/lib/rancher/k3s/server/node-token
On each worker:
curl -sfL https://get.k3s.io | K3S_URL=https://10.0.0.10:6443 K3S_TOKEN=<token> sh -
Use private/VPN addresses between nodes. If nodes communicate over the public internet, consider k3s’s built-in WireGuard backend for Flannel (--flannel-backend=wireguard-native) to encrypt pod traffic.
3. Deploy an app with an ingress
k3s includes Traefik as the ingress controller, listening on ports 80 and 443.
# whoami.yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: whoami }
spec:
replicas: 2
selector: { matchLabels: { app: whoami } }
template:
metadata: { labels: { app: whoami } }
spec:
containers:
- name: whoami
image: traefik/whoami
ports: [{ containerPort: 80 }]
---
apiVersion: v1
kind: Service
metadata: { name: whoami }
spec:
selector: { app: whoami }
ports: [{ port: 80, targetPort: 80 }]
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: { name: whoami }
spec:
rules:
- host: whoami.example.com
http:
paths:
- path: /
pathType: Prefix
backend: { service: { name: whoami, port: { number: 80 } } }
kubectl apply -f whoami.yaml
curl -H "Host: whoami.example.com" http://<server-ip>/
Point DNS at the server and the app is live on HTTP.
4. HTTPS with cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
Create a Let’s Encrypt ClusterIssuer (HTTP-01 via Traefik), then add cert-manager.io/cluster-issuer and a tls: section to your Ingress. Full walkthrough: cert-manager on Kubernetes.
5. Persistent storage
k3s’s local-path provisioner creates volumes on the node’s disk automatically:
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: data }
spec:
accessModes: [ReadWriteOnce]
storageClassName: local-path
resources: { requests: { storage: 5Gi } }
Data lives under /var/lib/rancher/k3s/storage/ on that node — back it up, and remember it doesn’t move if the pod is rescheduled to another node. For multi-node redundancy, look at Longhorn. See Kubernetes persistent volumes.
6. Access the cluster from your laptop
Copy /etc/rancher/k3s/k3s.yaml to your machine, replace 127.0.0.1 with the server address, and save it as ~/.kube/config. Better: don’t expose port 6443 publicly — reach it over a VPN like Tailscale or an SSH tunnel (ssh -L 6443:127.0.0.1:6443 user@server).
7. Disable bundled components (optional)
Prefer ingress-nginx or your own load balancer?
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable traefik --disable servicelb" sh -
8. Backups and upgrades
- Single-server k3s uses SQLite in
/var/lib/rancher/k3s/server/db/; back it up along with the node token. With embedded etcd (HA mode), usek3s etcd-snapshot. - Keep manifests in Git (GitOps with Argo CD) so the cluster can be rebuilt.
- Upgrade by re-running the install script with a newer version, one node at a time, or use the system-upgrade-controller.
Uninstall
/usr/local/bin/k3s-uninstall.sh # server
/usr/local/bin/k3s-agent-uninstall.sh # worker
k3s vs kubeadm vs Docker Compose
- Docker Compose — simplest for one server.
- k3s — real Kubernetes with minimal overhead; great for 1–10 nodes.
- kubeadm — standard upstream setup, more moving parts.
See Kubernetes vs Docker Compose. A 2–4 GB KVM VPS runs a single-node k3s cluster with several small apps comfortably.