Kubernetes

Lightweight Kubernetes with k3s on Domain India VPS (Single & Multi-Node)

By Domain India Team · DomainIndia EngineeringPublished 12 min read
Knowledge base article
Contents (15 sections)

k3s is a lightweight, fully conformant Kubernetes distribution that installs as a single binary. It lets you learn Kubernetes, or run a handful of real services, on one VPS or a small group of them. This guide covers a single-node install, a multi-node cluster, HTTPS ingress with Let's Encrypt, Helm, storage, monitoring and backups on a Domain India VPS.

Key takeaways

k3s gives you standard Kubernetes (kubectl, YAML manifests, Helm charts) with far less overhead than a full kubeadm cluster. Plan on at least 2 GB of RAM for a server node and 4 GB once you add monitoring. It needs root access, so it runs on a VPS, not on shared hosting. Install with one command, keep the API port private, and back up the datastore and the join token.

1. Why k3s instead of full Kubernetes

A kubeadm cluster runs etcd and several separate control-plane components, which is heavy for a single small server. k3s packages the essentials into one binary:

  • a single binary of well under 100 MB;
  • SQLite as the default datastore on a single server, with embedded etcd available for high availability;
  • a built-in service load balancer, the Traefik ingress controller and a local storage provisioner;
  • the same kubectl, the same manifests and the same Helm charts as any other conformant Kubernetes.

k3s is a CNCF project, originally created by Rancher (now part of SUSE). The k3s documentation gives 2 GB of RAM as the minimum for a server node and 512 MB for an agent (worker) node.

2. When to use k3s

Use caseGood fit?
Learning KubernetesExcellent
Small production app (1-5 services)Good
Edge and small-device deploymentsDesigned for it
Large microservice estates (hundreds of pods, many teams)Consider RKE2, upstream Kubernetes or a managed service

If you only need to run one app, a container on a plain VPS or the Domain India App Platform is simpler. Choose k3s when you want Kubernetes itself: several services, rolling deployments, Helm charts or practice for a Kubernetes job.

3. Single-node setup

Order a VPS with at least 4 GB of RAM if you plan to add monitoring, and choose Ubuntu 24.04 LTS or AlmaLinux 9 (or a newer LTS release if your OS list offers one). Choose no control panel: a panel's web server would take ports 80 and 443, which Traefik needs.

  1. Connect as root over SSH.
  2. Open the firewall for k3s.
    Keep your firewall on and add the rules below rather than disabling it.
  3. Install k3s. Run `curl -sfL https://get.k3s.io
    sh -. The installer sets up a systemd service called k3s`.
  4. Check the node.
    Run sudo kubectl get nodes. The node should show Ready within a minute.
  5. Use kubectl from your computer (optional).
    Copy /etc/rancher/k3s/k3s.yaml to ~/.kube/config on your computer and replace 127.0.0.1 in the server: line with your VPS IP. Only do this if port 6443 is restricted to your own IP address.

Firewall rules on AlmaLinux (firewalld):

bash
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --zone=trusted --add-source=10.42.0.0/16   # pods
sudo firewall-cmd --permanent --zone=trusted --add-source=10.43.0.0/16   # services
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="YOUR.OFFICE.IP/32" port port="6443" protocol="tcp" accept'
sudo firewall-cmd --reload

On Ubuntu (ufw):

bash
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 10.42.0.0/16 to any
sudo ufw allow from 10.43.0.0/16 to any
sudo ufw allow from YOUR.OFFICE.IP to any port 6443 proto tcp
sudo ufw enable

Check the node:

bash
sudo kubectl get nodes
# NAME            STATUS   ROLES                  AGE   VERSION
# your-vps-name   Ready    control-plane,master   30s   v1.xx.x+k3s1
k3s runs as root by default

The k3s service runs as root and the admin kubeconfig has full cluster rights. If other people will use the cluster, give them namespaces and RBAC roles instead of copying k3s.yaml. A rootless mode exists but has limitations; read the k3s documentation before using it.

4. Deploy your first app

Create app.yaml:

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:
          requests:
            memory: 64Mi
            cpu: 50m
          limits:
            memory: 128Mi
            cpu: 100m
---
apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector: { app: hello }
  ports:
  - port: 80
    targetPort: 80

Apply it:

bash
sudo kubectl apply -f app.yaml
sudo kubectl get pods
# hello-7c5cbb5d4f-abc12   1/1   Running   0   10s
# hello-7c5cbb5d4f-def34   1/1   Running   0   10s

In production, pin images to a specific version tag instead of latest.

5. Install Helm

Helm installs packaged applications ("charts") such as cert-manager, Prometheus and Longhorn:

bash
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
helm version

A newer Helm major version may be available; check the Helm website. Charts from the Bitnami catalogue changed their free-image terms in 2025, so check a chart's image source and update policy before you rely on it. For a gentle introduction, see Helm charts for beginners.

6. HTTPS ingress with Traefik and Let's Encrypt

k3s ships with the Traefik ingress controller listening on ports 80 and 443. The most portable way to get Let's Encrypt certificates is cert-manager, which works the same whichever Traefik version your k3s release bundles.

Install cert-manager:

bash
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager --create-namespace \
  --set crds.enabled=true

Create a Let's Encrypt issuer, issuer.yaml:

yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef:
      name: letsencrypt-account-key
    solvers:
    - http01:
        ingress:
          ingressClassName: traefik

Then define your Ingress, ingress.yaml:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  ingressClassName: traefik
  rules:
  - host: hello.yourcompany.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: hello
            port: { number: 80 }
  tls:
  - hosts: [hello.yourcompany.com]
    secretName: hello-tls

Point an A record for hello.yourcompany.com at your VPS IP, then apply both files with sudo kubectl apply -f. cert-manager requests the certificate once DNS resolves; sudo kubectl get certificate shows READY True when it is issued. To redirect HTTP to HTTPS, add a Traefik redirectScheme middleware to the Ingress.

7. Multi-node cluster

For more capacity, add worker (agent) nodes. On the first server, read the join token:

bash
sudo cat /var/lib/rancher/k3s/server/node-token

On each worker VPS:

bash
curl -sfL https://get.k3s.io | \
  K3S_URL=https://<server-ip>:6443 \
  K3S_TOKEN=<token-from-server> \
  sh -

Then check from the server with sudo kubectl get nodes.

Nodes talk to each other over the flannel network: UDP 8472 for the default VXLAN backend. That traffic is not encrypted and the port must never be open to the whole internet. When your nodes connect over public IPs, install every node with --flannel-backend=wireguard-native (UDP 51820 between nodes), and allow the node ports only from your other nodes' IPs. Workers must also reach the server on TCP 6443.

High-availability control plane (optional)

Three server nodes with embedded etcd keep the cluster running if one server fails. Pick a long random token, then:

bash
# First server
curl -sfL https://get.k3s.io | K3S_TOKEN=<shared-secret> sh -s - server --cluster-init

# Second and third servers
curl -sfL https://get.k3s.io | K3S_TOKEN=<shared-secret> sh -s - server \
  --server https://<first-server-ip>:6443

The servers must reach each other on TCP 2379-2380 for etcd. Use an odd number of servers (3 or 5). As an alternative, k3s can use an external PostgreSQL or MySQL datastore with --datastore-endpoint instead of embedded etcd.

8. Persistent storage

k3s includes the local-path storage class, which stores volumes on the node's own disk:

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: local-path
  resources:
    requests: { storage: 5Gi }

A local-path volume lives on one node, so a pod that uses it can only run there. For replicated storage across several nodes, Longhorn is a common choice with k3s. It needs open-iscsi on every node and is most useful with three or more nodes:

bash
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace

9. Monitoring with Prometheus and Grafana

bash
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace

This stack uses a lot of memory; on a 4 GB node leave room for your apps and set resource limits. Open Grafana through a port-forward rather than exposing it:

bash
sudo kubectl -n monitoring port-forward svc/monitoring-grafana 3000:80
sudo kubectl -n monitoring get secret monitoring-grafana \
  -o jsonpath="{.data.admin-password}" | base64 -d

Don't expose Grafana as a LoadBalancer service on a single node: k3s's built-in load balancer would try to bind port 80, which Traefik already uses. For logs and alerting, see production observability with Prometheus, Grafana and Loki.

10. Backups

Back up the cluster datastore, the server token and your persistent volumes.

  • Single server (SQLite): the state is in /var/lib/rancher/k3s/server/db/. Copy it while k3s is stopped, or with sqlite3 state.db ".backup /root/state-backup.db", and also keep /var/lib/rancher/k3s/server/token: a restored datastore is useless without it.
  • Embedded etcd: k3s takes etcd snapshots automatically; run sudo k3s etcd-snapshot save for an on-demand one before upgrades.
  • Volumes: back up local-path data under /var/lib/rancher/k3s/storage/, or use Longhorn's backup feature.

A nightly example that ships an archive off the server with rclone. Save it as /root/k3s-backup.sh and make it executable:

bash
#!/bin/sh
set -e
tar czf /root/k3s-backup.tgz \
  /var/lib/rancher/k3s/server/db \
  /var/lib/rancher/k3s/server/token \
  /var/lib/rancher/k3s/storage
rclone copy /root/k3s-backup.tgz remote:k3s-backups/

Then run it from root's crontab with 0 3 * * * /root/k3s-backup.sh. See our automated backups guide for rclone setup, and test a restore before you rely on it.

11. Security hardening

  • Keep the API private. Allow port 6443 only from your own IPs and your other nodes, never from everywhere.
  • Use RBAC. It is on by default; give people namespace roles, not cluster-admin.
  • Restrict pod traffic with NetworkPolicies; k3s enforces them out of the box.
  • Scan images for known vulnerabilities, for example with trivy image your-app:1.2.3.
  • Keep k3s updated. Re-run the installer with the same options (or put them in /etc/rancher/k3s/config.yaml), pinning a release with INSTALL_K3S_VERSION or following INSTALL_K3S_CHANNEL=stable. Upgrade servers before agents, one minor version at a time.
  • Harden the VPS itself. See the VPS security checklist.

12. Common pitfalls

"No space left on device"
Container images fill /var/lib/rancher/k3s/agent/containerd/. Remove unused images with sudo k3s crictl rmi --prune.
Ingress not reachable
Traefik binds host ports 80 and 443. Check that your firewall allows them and that no other web server is running on the VPS.
Flannel port open to the internet
UDP 8472 exposes the cluster network. Allow it only between your nodes, or use the WireGuard backend.
Busy cluster on SQLite
Heavy write loads suit embedded etcd or an external database better than the default SQLite datastore.
Running out of RAM
Monitoring, logging and apps together fill 4 GB fast. Set requests and limits on every workload.
Messy uninstall
Use /usr/local/bin/k3s-uninstall.sh (or k3s-agent-uninstall.sh on workers), not manual deletion.

13. Running k3s on Domain India

k3s needs root access and its own ports, so it runs on a Domain India VPS, not on shared hosting. Our VPS plans are self-managed KVM servers with full root access: you install and look after k3s yourself, and cPanel is not offered on VPS. For a cluster with monitoring, start with at least 4 GB of RAM.

VPS Basic
₹1,105.30/mo + GST
  • 2 vCPU
  • 4 GB DDR4 RAM
  • 128 GB NVMe SSD Storage
  • 3 TB Monthly Bandwidth
See plan details

The plan card shows the Domain India list price, excluding 18% GST. If you just want to deploy one app from a Git repository without running Kubernetes yourself, look at the App Platform.

Frequently asked questions

Can I use the same kubectl commands I learned on EKS or GKE?

Yes. k3s is a conformant Kubernetes distribution, so kubectl, manifests and Helm charts work the same way. Features tied to a cloud provider, such as its load balancers and storage drivers, are different.

Does k3s work with public container images?

Yes. It pulls from Docker Hub, GitHub Container Registry and other registries. For private registries, create an image pull secret or configure /etc/rancher/k3s/registries.yaml.

Can I migrate from k3s to another Kubernetes distribution later?

Yes. Your manifests and Helm charts move across. You may need to change storage classes and the ingress controller to match the new cluster.

How much RAM does k3s need?

The k3s documentation gives 2 GB as the minimum for a server node and 512 MB for an agent node. With Prometheus, Grafana and your own apps, plan on 4 GB or more.

Can I run k3s on shared hosting?

No. k3s needs root access, its own system service and its own network ports, which shared hosting does not provide. Use a VPS, or the App Platform if you only need to run an app.

How do k3s, MicroK8s and minikube compare?

k3s is a lightweight distribution built for production and edge use. MicroK8s is Canonical's snap-based distribution. minikube is designed for local development and testing, not production.

Ready to build your cluster? Compare our VPS plans, read how to access your VPS using SSH, or open a support ticket if you have a question about your server.

Run Kubernetes on your own VPS

Self-managed KVM VPS with full root access, ready for k3s, Docker and your own tools.

See VPS plans

Was this article helpful?

Your answer helps us decide what to improve next.

Still need help? Open a support ticket and our team will reply.

Prefer an app? Add this site to your home screen.Get the app