How to Set Up k3s Kubernetes on a VPS: Lightweight Orchestration for Production Workloads
Full Kubernetes requires 2 GB RAM just for the control plane — too heavy for most VPS use cases. k3s is a CNCF-certified Kubernetes distribution from Rancher Labs that bundles everything into a 70 MB binary, uses SQLite instead of etcd, and runs with as little as 512 MB RAM. It is fully Kubernetes-compatible: the same kubectl commands, YAML manifests, Helm charts, and ecosystem tools all work without modification.
When k3s Makes Sense Over Docker Compose
- You want Kubernetes-native tooling (kubectl, Helm, ArgoCD) without managed Kubernetes cost
- You need rolling deployments, readiness/liveness probes, and automatic pod restarts out of the box
- You are learning Kubernetes for cloud deployments and want a low-cost real environment
- You plan to scale to a multi-node cluster in the future
- You want to run Helm charts (many tools only publish Helm charts, not Docker Compose files)
Resource Comparison: k3s vs Full Kubernetes
| Component | Full Kubernetes | k3s |
|---|---|---|
| Control plane RAM | ~2–3 GB | ~200–300 MB |
| Binary size | Multiple large binaries | 70 MB single binary |
| Database | etcd (memory-heavy) | SQLite (default) or etcd |
| Minimum server RAM | 2 GB + 2 GB per worker | 512 MB server, 256 MB worker |
| Kubernetes compatible | Yes | Yes (CNCF certified) |
Step 1: Install k3s (Single Node)
curl -sfL https://get.k3s.io | sh -
# Verify k3s is running
sudo systemctl status k3s
sudo k3s kubectl get nodes
# Expected output:
# NAME STATUS ROLES AGE VERSION
# your-vps Ready control-plane,master 1m v1.30.x+k3s1
Step 2: Configure kubectl on Your Local Machine
# Copy kubeconfig from VPS
scp root@YOUR_VPS_IP:/etc/rancher/k3s/k3s.yaml ~/.kube/config-k3s
# Update server address (defaults to 127.0.0.1 — change to VPS IP)
sed -i 's/127.0.0.1/YOUR_VPS_IP/g' ~/.kube/config-k3s
export KUBECONFIG=~/.kube/config-k3s
# Test remote connection
kubectl get nodes
Step 3: Deploy Your First Application
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: nginx:alpine
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "200m"
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 80
kubectl apply -f myapp-deployment.yaml
kubectl get pods
kubectl get services
Step 4: Traefik Ingress with Let’s Encrypt SSL
k3s includes Traefik as the default Ingress controller. Configure Let’s Encrypt:
nano traefik-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
ports:
web:
redirectTo:
port: websecure
certResolvers:
letsencrypt:
email: admin@yourdomain.com
storage: /data/acme.json
httpChallenge:
entryPoint: web
kubectl apply -f traefik-config.yaml
Expose your application via an Ingress resource:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
spec:
rules:
- host: myapp.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
kubectl apply -f ingress.yaml
# Traefik automatically requests a Let's Encrypt certificate for myapp.yourdomain.com
Step 5: Rolling Deployments and Rollbacks
# Update to new image version — triggers rolling deployment
kubectl set image deployment/myapp myapp=myimage:v2
# Watch the rolling update progress
kubectl rollout status deployment/myapp
# Roll back if the new version has issues
kubectl rollout undo deployment/myapp
# View deployment history
kubectl rollout history deployment/myapp
Step 6: Add a Worker Node
# On the SERVER node: get the join token
sudo cat /var/lib/rancher/k3s/server/node-token
# On a SECOND VPS (the worker):
curl -sfL https://get.k3s.io | \
K3S_URL=https://SERVER_VPS_IP:6443 \
K3S_TOKEN=YOUR_NODE_TOKEN sh -
# Verify the worker joined
kubectl get nodes
# NAME STATUS ROLES
# server-vps Ready control-plane,master
# worker-vps Ready <none>
Essential kubectl Commands
kubectl get all -A # View all resources across namespaces
kubectl logs -f deployment/myapp # Stream pod logs
kubectl exec -it deployment/myapp -- /bin/sh # Shell into running pod
kubectl top pods # Resource usage per pod
kubectl scale deployment myapp --replicas=4 # Scale deployment
kubectl delete deployment myapp # Delete deployment
Getting Started
k3s runs on a VPS with as little as 1 GB RAM, though 2 GB is comfortable for a control plane plus application workloads. KVM VPS plans at VPS.DO fully support k3s — KVM provides the real kernel namespace isolation that Kubernetes requires. NVMe storage accelerates container image pulls and persistent volume I/O. The 2 vCPU / 2 GB plan handles a single-node k3s cluster with 5–10 small applications running simultaneously.
Conclusion
k3s brings production Kubernetes capabilities — rolling deployments, readiness probes, pod auto-restart, Ingress with SSL, and Helm chart support — to a VPS at a fraction of full Kubernetes’s resource requirements. For teams familiar with Kubernetes or planning cloud migration, k3s provides consistent tooling between local development and production VPS deployment at far lower cost than managed Kubernetes services like EKS, GKE, or AKS.