Lightweight Kubernetes with k3s on DomainIndia VPS (Single & Multi-Node)
Why k3s instead of full Kubernetes
Standard Kubernetes (kubeadm) needs 2 GB+ RAM per node just for the control plane. k3s strips down to essentials:
- Single binary (~60 MB)
- Built-in SQLite (no etcd for single-node)
- Built-in load balancer, Ingress (Traefik), local storage provisioner
- Works on 512 MB RAM VPS (though 2 GB is practical minimum)
Same kubectl, same YAML, same Helm charts. Production-ready — k3s powers AWS EKS Anywhere, Rancher, and edge deployments.
When to use k3s
| Use case | Good fit? |
|---|---|
| Learning Kubernetes | Excellent |
| Small production app (1-5 services) | Great |
| Edge / IoT deployments | Designed for it |
| Large-scale microservices (100+ pods) | Upgrade to full K8s or managed (EKS, GKE) |
For most DomainIndia VPS customers: k3s is enough. Only upgrade to full K8s when you outgrow it.
Single-node setup
Order a 4 GB+ DomainIndia VPS (AlmaLinux 9 or Ubuntu 22.04 recommended).
- SSH as root
- Disable firewalld temporarily (we'll reconfigure after):
```bash
sudo systemctl disable --now firewalld
```
- Install k3s:
```bash
curl -sfL https://get.k3s.io | sh -
```
- Verify:
```bash
sudo kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# your-vps-name Ready control-plane,master 30s v1.30.0+k3s1
```
- Copy kubeconfig for your laptop:
```bash
sudo cat /etc/rancher/k3s/k3s.yaml
# Copy to ~/.kube/config on laptop, replace "server: https://127.0.0.1:6443"
# with your VPS public IP
```
By default k3s runs as root. For multi-tenant use, install rootless k3s or use RBAC to restrict namespaces.
Deploy your first app
Create app.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
replicas: 2
selector:
matchLabels: { app: hello }
template:
metadata:
labels: { app: hello }
spec:
containers:
- name: hello
image: nginxdemos/hello:latest
ports:
- containerPort: 80
resources:
limits:
memory: 128Mi
cpu: 100m
---
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
selector: { app: hello }
ports:
- port: 80
targetPort: 80Apply:
kubectl apply -f app.yaml
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# hello-7c5cbb5d4f-abc12 1/1 Running 0 10s
# hello-7c5cbb5d4f-def34 1/1 Running 0 10sIngress with SSL (Traefik + Let's Encrypt)
k3s ships with Traefik ingress controller. Configure it to get Let's Encrypt certs automatically.
Edit /var/lib/rancher/k3s/server/manifests/traefik-config.yaml:
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
ports:
web:
redirectTo:
port: websecure
additionalArguments:
- --certificatesresolvers.letsencrypt.acme.email=admin@yourcompany.com
- --certificatesresolvers.letsencrypt.acme.storage=/data/acme.json
- --certificatesresolvers.letsencrypt.acme.tlschallenge=trueWait 30 seconds for Traefik to reload.
Then define your Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
spec:
rules:
- host: hello.yourcompany.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello
port: { number: 80 }
tls:
- hosts: [hello.yourcompany.com]Point DNS hello.yourcompany.com → your VPS IP. Traefik obtains SSL cert on first visit.
Multi-node cluster
For HA or scaling beyond one VPS:
Control plane (first node)
Install k3s + note the token:
# On VPS 1 (master)
curl -sfL https://get.k3s.io | sh -
sudo cat /var/lib/rancher/k3s/server/node-token
# prints: K10xxxxx::server:abc123...Worker nodes
# On VPS 2, 3, ... (workers)
curl -sfL https://get.k3s.io |
K3S_URL=https://<master-vps-ip>:6443
K3S_TOKEN=K10xxxxx::server:abc123...
sh -Verify from master:
sudo kubectl get nodes
# NAME STATUS ROLES AGE
# master Ready control-plane,master 1h
# worker-1 Ready <none> 30s
# worker-2 Ready <none> 30sHA control plane (optional)
For >99% uptime, run 3 control-plane nodes with external DB (etcd or PostgreSQL):
curl -sfL https://get.k3s.io | sh -s - server
--cluster-init
--datastore-endpoint "postgres://k3s:[email protected]:5432/k3s"Then join other masters with same token.
Persistent storage
k3s ships with local-path-provisioner — provisions HostPath volumes automatically.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes: [ReadWriteOnce]
storageClassName: local-path
resources:
requests: { storage: 5Gi }For multi-node with shared storage, use Longhorn (k3s-friendly distributed storage):
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.6.0/deploy/longhorn.yamlHelm charts
Install anything from Helm Hub (Redis, PostgreSQL, Prometheus, Grafana):
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install redis bitnami/redis --set auth.password=yourpass
# Use in deployment:
# REDIS_URL=redis://:yourpass@redis-master:6379/0Monitoring with Prometheus + Grafana
One-liner install:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install monitoring prometheus-community/kube-prometheus-stack
--set grafana.service.type=LoadBalancer
--set prometheus.prometheusSpec.resources.requests.memory=512MiExpose Grafana via Ingress + get the admin password:
kubectl get secret monitoring-grafana -o jsonpath="{.data.admin-password}" | base64 -dBackups
Back up etcd + PVs regularly.
For single-node k3s, /var/lib/rancher/k3s/server/db/state.db is your state — cron-rsync to S3:
0 3 * * * tar czf /tmp/k3s-backup.tgz /var/lib/rancher/k3s/server/db /var/lib/rancher/k3s/storage &&
rclone copy /tmp/k3s-backup.tgz remote:k3s-backups/See our Automated Backups guide.
Security hardening
- Change the k3s API port from 6443 if exposed; firewall to trusted IPs only
- Use Network Policies to restrict pod-to-pod traffic
- Enable RBAC (default in k3s); don't give users cluster-admin
- Scan images for CVEs:
trivy image your-app:latest - Keep k3s updated:
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.30.X+k3s1 sh -
Common pitfalls
FAQ
kubectl commands I learned for EKS/GKE?
Yes — 95%+ compatible. Some features (CSI drivers, cloud-specific integrations) differ.
Yes — pulls from Docker Hub, GHCR, GCR, etc. For private registries, add secrets.
Yes — kubectl manifests transfer. You may need to adjust storage classes and ingress controller choice.
Up to ~50 nodes tested. For >50, consider RKE2 or upstream Kubernetes.
k3s: production-ready, minimal overhead. MicroK8s: Ubuntu-friendly, snap-based. minikube: dev/test only, don't run in production.
Kubernetes on a DomainIndia VPS — start lean with k3s. View VPS plans