Introduction
In fifteen minutes you can have a working Kubernetes cluster on a single Ubuntu machine, using less than 1GB of memory. That is what k3s is for. k3s is a fully conformant Kubernetes distribution built for edge, IoT, and development environments, but it works just as well as a lightweight production cluster for small workloads.
By the end of this guide you will have k3s installed on Ubuntu 24.04 (or 22.04), a working kubectl context, a sample Nginx deployment exposed through k3s's built-in Traefik ingress, and the knowledge to add agent nodes or tear the cluster down cleanly. This guide is for developers and junior DevOps engineers who understand basic Linux and want Kubernetes without the weight of a full kubeadm cluster.
Prerequisites
- Ubuntu 22.04 or 24.04 (x86_64) — server or desktop, fresh is best
- sudo access — every install step needs root
- 1-2 GB of free RAM — k3s itself needs roughly 512MB; your workloads need the rest
- At least 1 CPU core and 5GB of free disk
- Ports 6443, 8472 (UDP), and 10250 open if you plan to add agent nodes later
- curl installed —
sudo apt update && sudo apt install -y curl
That last command installs curl, the only extra dependency you need before starting. k3s bundles containerd, so you do not need Docker or any container runtime preinstalled.
Step 1 — Install k3s on the server node
Run the official k3s install script with curl piped to sh:
curl -sfL https://get.k3s.io | sh -
This downloads the k3s binary, installs it as a systemd service named k3s, and starts a single-node cluster. It is the single canonical install command for k3s and it works on every supported Ubuntu release.
Step 2 — Check that the k3s service is running
sudo systemctl status k3s
Look for active (running) in the output. If the service is still starting, give it 30-60 seconds on the first boot while containerd images pull. You can also confirm it starts on reboot:
sudo systemctl is-enabled k3s
This should print enabled, meaning k3s will come back automatically after a reboot.
Step 3 — Verify the node is registered
k3s ships its own kubectl. Ask it to list the nodes in your cluster:
sudo k3s kubectl get nodes
Expect one row with STATUS Ready and ROLE control-plane,master. On a single-node install, this is your entire cluster — one control plane that also runs workloads.
Step 4 — Set up kubectl access without sudo
The cluster's kubeconfig lives at /etc/rancher/k3s/k3s.yaml and is root-owned. Copy it to your user and fix the ownership:
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
chmod 600 ~/.kube/config
Now plain kubectl (if installed) or k3s kubectl works without sudo. To skip copying, you can instead export the path:
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
sudo -E k3s kubectl get nodes
The KUBECONFIG variable tells kubectl where to find cluster credentials. The copy-and-chown approach from the first command block is the one to keep for daily use.
Step 5 — Deploy a sample workload
Create a deployment running Nginx:
sudo k3s kubectl create deployment nginx --image=nginx
This pulls the official nginx image and starts one replica. Check that the pod is running:
sudo k3s kubectl get pods -w
Wait until the pod shows STATUS Running and READY 1/1, then press Ctrl+C to stop watching. The -w flag streams updates live so you can see the pod go from ContainerCreating to Running.
Step 6 — Expose it as a Service
sudo k3s kubectl expose deployment nginx --port=80 --type=NodePort
This creates a Service of type NodePort pointing at port 80 of your Nginx pods, reachable on a high port (30000-32767) on every cluster node. Find the assigned port:
sudo k3s kubectl get svc nginx
Open http://<your-server-ip>:<nodeport> in a browser — you should see the Nginx welcome page. That confirms traffic flows end to end.
Step 7 — Route traffic through the built-in Traefik ingress
k3s ships with Traefik preinstalled as the default ingress controller, so you do not need to install anything. Create an ingress resource that routes a hostname to your service:
sudo k3s kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
spec:
rules:
- host: nginx.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
EOF
The heredoc applies an Ingress object pointing nginx.local at your service. Add <server-ip> nginx.local to your local /etc/hosts, then visit http://nginx.local — Traefik routes the request into the cluster.
Verify it worked
Run this single command for a full cluster health snapshot:
sudo k3s kubectl get nodes,pods,svc,ingress
You are done when: the node is Ready, the nginx pod is Running, the service has a ClusterIP and NodePort, and the ingress has an address. If all four show green, your 15-minute Kubernetes cluster is fully operational.
Add a second node (optional)
To join an agent node, you need the server's node token. On the server:
sudo cat /var/lib/rancher/k3s/server/node-token
On the new Ubuntu machine, run the install script as an agent pointing at the server:
curl -sfL https://get.k3s.io | K3S_URL=https://<server-ip>:6443 K3S_TOKEN=<token> sh -
The K3S_URL variable tells the agent where the server's API listens (port 6443); the token authenticates it. Re-run sudo k3s kubectl get nodes on the server — the new agent appears as a second row.
Common issues & fixes
1. "Permission denied" on /etc/rancher/k3s/k3s.yaml
You ran kubectl without sudo before copying the kubeconfig. Either prefix with sudo, export KUBECONFIG as shown in Step 4, or copy the config to ~/.kube/config with your user owning it.
2. Node stays NotReady for minutes
Usually the CNI network is still initializing on first boot. Check with:
sudo journalctl -u k3s --since "10 min ago" | tail -50
If you see flannel or calico errors, the usual cause is a firewall blocking UDP 8472 between nodes (single-node installs are unaffected). Wait 2-3 minutes, then reboot if it persists.
3. curl install script hangs or fails with TLS errors
The box cannot reach get.k3s.io. Test connectivity:
curl -sIL https://get.k3s.io | head -5
No response means a network or proxy problem — check DNS, corporate proxies, or ufw outbound rules before retrying.
4. Agent node fails to join: "failed to get CA certs"
The agent cannot reach the server on port 6443. Verify the server is listening and reachable:
curl -sk https://<server-ip>:6443/version
If this times out, open port 6443 on the server's firewall (sudo ufw allow 6443/tcp) and double-check that K3S_TOKEN matches the server's node-token file exactly.
5. Pods stuck in ImagePullBackOff
The node cannot pull images from the registry. Test with curl -sI https://registry-1.docker.io. If you are behind a proxy, k3s needs the proxy in its environment: create /etc/systemd/system/k3s.service.env with your HTTP_PROXY/HTTPS_PROXY values, run sudo systemctl daemon-reload && sudo systemctl restart k3s.
Uninstall cleanly
When you are done experimenting, remove everything with the bundled script:
/usr/local/bin/k3s-uninstall.sh
This stops the service, removes the binary, deletes cluster data under /var/lib/rancher/k3s, and cleans up networking. For agent nodes the script is /usr/local/bin/k3s-agent-uninstall.sh.
Conclusion
You now have a real, conformant Kubernetes cluster on Ubuntu — installed with one command, verified with kubectl, serving traffic through Traefik, and ready to scale to extra nodes when you need them. k3s is the fastest path from a bare VPS to a working cluster, and everything you learned here transfers directly to full-size Kubernetes.
Need help setting up or hardening a Kubernetes cluster on your own servers? XpertBees offers professional server setup and DevOps services — reach out through the contact page and we will get your infrastructure production-ready.



