How to Set Up Kubernetes with k3s
Set up a lightweight Kubernetes cluster with k3s on Linux: single-node or multi-node, kubectl config, and your first real deployment in under 30 minutes.
Before you start
- ▸Root or sudo access on all nodes
- ▸Unique hostnames and static or stable IPs assigned to each node
- ▸Outbound HTTPS access to get.k3s.io and container registries
- ▸kubectl installed on your local workstation for remote management
k3s is a CNCF-certified, lightweight Kubernetes distribution from Rancher that ships as a single binary under 100 MB. It strips out legacy and cloud-specific drivers, bundles containerd, CoreDNS, Traefik, and a local-path storage provisioner, and runs comfortably on a 1 GB RAM server. Whether you are deploying a single-node homelab cluster or a multi-node edge setup, k3s gets you to a working Kubernetes environment in minutes rather than hours.
Prerequisites and Network Planning
Before running a single command, settle these details:
- OS: Any modern 64-bit Linux. k3s works best on Ubuntu 22.04/24.04 LTS, Fedora 39+, or RHEL/Rocky 9. The kernel must be 5.4 or newer.
- Resources (per node): 1 vCPU and 512 MB RAM minimum for a server node; agents can run lighter. For real workloads, 2 vCPU and 2 GB RAM is comfortable.
- Hostnames: Set unique, stable hostnames on every node before installation—k3s registers nodes by hostname.
- Firewall: If firewalld or ufw is active, open the ports listed below before starting k3s, or the agent nodes will not join.
- No existing Kubernetes: Remove any prior kubeadm, minikube, or k3s installations first.
Required Ports
| Port | Protocol | Purpose | Open On |
|---|---|---|---|
| 6443 | TCP | Kubernetes API server | Server node |
| 8472 | UDP | Flannel VXLAN overlay | All nodes |
| 10250 | TCP | kubelet metrics | All nodes |
| 51820 | UDP | WireGuard (if enabled) | All nodes |
Open Ports — firewalld (Fedora/RHEL/Rocky)
sudo firewall-cmd --permanent --add-port=6443/tcp
sudo firewall-cmd --permanent --add-port=8472/udp
sudo firewall-cmd --permanent --add-port=10250/tcp
sudo firewall-cmd --reload
Open Ports — ufw (Ubuntu/Debian)
sudo ufw allow 6443/tcp
sudo ufw allow 8472/udp
sudo ufw allow 10250/tcp
sudo ufw reload
Step 1 — Install k3s on the Server Node
The official installer script pulls the latest stable release, sets up a systemd service, and writes a kubeconfig to /etc/rancher/k3s/k3s.yaml. Run this on the machine that will act as your control plane.
curl -sfL https://get.k3s.io | sh -
The script detects your init system (systemd), installs the k3s binary to /usr/local/bin, and enables and starts the k3s service immediately. To pin a specific version, set the environment variable before piping:
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.30.2+k3s1" sh -
Verify the Server is Running
sudo systemctl status k3s
sudo k3s kubectl get nodes
Output will show one node in Ready state with role control-plane,master. It may take 30–60 seconds to reach Ready on a slow VM.
Retrieve the Node Token
Agent nodes authenticate using a token stored on the server. Grab it now.
sudo cat /var/lib/rancher/k3s/server/node-token
Copy this string — you will paste it on every agent node.
Step 2 — Add Agent Nodes (Multi-Node Setup)
On each machine that will act as a worker, run the installer with the K3S_URL and K3S_TOKEN variables set. Replace the placeholders with your server's IP and the token from Step 1.
curl -sfL https://get.k3s.io | \
K3S_URL="https://<SERVER_IP>:6443" \
K3S_TOKEN="<NODE_TOKEN>" \
sh -
This installs and starts the k3s-agent systemd service. The agent connects to the server API, registers itself, and joins the cluster. Repeat for every additional worker node.
Confirm All Nodes Joined
Back on the server node:
sudo k3s kubectl get nodes -o wide
Every agent should appear as Ready within about a minute. If a node stays NotReady, check its agent service: sudo journalctl -u k3s-agent -f.
Step 3 — Configure kubectl on Your Workstation
k3s installs its own kubectl wrapper, but you will want the standard kubectl binary on your workstation to manage the cluster remotely.
Install kubectl
# Debian/Ubuntu
sudo apt-get install -y kubectl
# Fedora/RHEL/Rocky — kubectl is in the Kubernetes repo
sudo dnf install -y kubectl
# Arch
sudo pacman -S kubectl
Copy the Kubeconfig
k3s writes the kubeconfig to /etc/rancher/k3s/k3s.yaml with 127.0.0.1 as the server address. Copy it to your workstation and update the address to the server's actual IP.
# On the server node — print and copy the content
sudo cat /etc/rancher/k3s/k3s.yaml
# On your workstation — paste into the kubeconfig and fix the server address
mkdir -p ~/.kube
# paste or scp the file:
scp user@<SERVER_IP>:/etc/rancher/k3s/k3s.yaml ~/.kube/config
# replace 127.0.0.1 with the real server IP
sed -i 's/127\.0\.0\.1/<SERVER_IP>/g' ~/.kube/config
chmod 600 ~/.kube/config
kubectl get nodes
You should see all nodes without using sudo or the k3s prefix.
Step 4 — Deploy Your First Application
Deploy a simple Nginx web server using a Kubernetes Deployment and expose it with a Service. This confirms that scheduling, networking, and DNS all work end to end.
Create the Deployment
kubectl create deployment nginx --image=nginx:stable-alpine --replicas=2
Expose It as a NodePort Service
kubectl expose deployment nginx --type=NodePort --port=80
Check the Assigned Port
kubectl get svc nginx
The output shows a NodePort in the 30000–32767 range. Hit any node's IP on that port to reach Nginx. For example, if the port is 31240:
curl http://<ANY_NODE_IP>:31240
Watch Pod Status
kubectl get pods -o wide -w
Both replicas should reach Running status. The -o wide flag shows which node each pod landed on, useful for verifying spread across workers.
Step 5 — Deploy with a Manifest File
Imperative commands are fine for testing, but production work uses YAML manifests. Here is a complete example you can apply directly.
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-k3s
spec:
replicas: 3
selector:
matchLabels:
app: hello-k3s
template:
metadata:
labels:
app: hello-k3s
spec:
containers:
- name: hello
image: nginxdemos/hello:plain-text
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: hello-k3s
spec:
selector:
app: hello-k3s
type: NodePort
ports:
- port: 80
targetPort: 80
EOF
kubectl rollout status deployment/hello-k3s
Verification and Health Checks
# Cluster component status
kubectl get componentstatuses
# All system pods — CoreDNS, Traefik, local-path-provisioner should be Running
kubectl get pods -n kube-system
# Node resource usage (requires metrics-server; k3s includes it by default)
kubectl top nodes
Troubleshooting
Agent Node Stays NotReady
Check the agent journal for TLS or connectivity errors. The most common causes are a wrong token, firewall blocking port 6443, or a clock skew greater than two minutes between nodes.
sudo journalctl -u k3s-agent --since "5 minutes ago"
Pods Stuck in Pending
Usually insufficient resources or a taint on the only node. Describe the pod to see the scheduler's reason:
kubectl describe pod <pod-name>
On a single-node cluster the server node is also a worker by default, so Pending pods are almost always a resource or image-pull issue rather than a scheduling one.
kubeconfig Permission Errors
If kubectl returns a permission denied error reading the kubeconfig, fix the file mode:
chmod 600 ~/.kube/config
Uninstalling k3s
The installer places uninstall scripts on the system. On the server:
sudo /usr/local/bin/k3s-uninstall.sh
On agent nodes:
sudo /usr/local/bin/k3s-agent-uninstall.shFrequently asked questions
- Does k3s support high-availability control plane?
- Yes. k3s supports embedded etcd for HA by initializing the first server with --cluster-init and joining additional server nodes with --server. You need an odd number (3 or 5) of server nodes for quorum.
- Can I use k3s with an external database instead of embedded SQLite?
- Yes. Pass --datastore-endpoint with a PostgreSQL, MySQL, or etcd connection string when starting the server. This is recommended for any production or HA deployment.
- Does k3s work on ARM (Raspberry Pi)?
- Yes, k3s ships ARM64 and ARMv7 binaries. The installer script detects the architecture automatically. Ubuntu Server 22.04 LTS for Raspberry Pi is a well-tested combination.
- How is k3s different from k0s or kubeadm?
- k3s bundles all dependencies including containerd, CoreDNS, and an ingress controller into one binary and automates TLS bootstrapping. kubeadm is the upstream tool that installs full Kubernetes but requires you to manage each component separately. k0s is a similar single-binary approach but without the opinionated bundled add-ons.
- How do I upgrade k3s to a newer version?
- Re-run the installer script with INSTALL_K3S_VERSION set to the target version on the server first, wait for it to restart, then repeat on each agent node. The Rancher System Upgrade Controller can automate this in a cluster-native way.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.