Skip to main content

Kubernetes on a VPS: A Lightweight Cluster with k3s

Cloud & DevOps · 19.04.2026 · 5 min read
Illustration for “Kubernetes on a VPS: A Lightweight Cluster with k3s”

Kubernetes on a VPS: A Minimal Cluster with k3s

k3s is a lightweight Kubernetes distribution from Rancher Labs that requires only 512 MB of RAM. It is ideal for learning K8s and running small production clusters on a VPS with 4 GB of RAM or more.

Installing k3s (Master Node)

curl -sfL https://get.k3s.io | sh -
systemctl status k3s

# Check the cluster
kubectl get nodes
kubectl get pods -A

Adding a Worker Node

# Get the token on the Master
cat /var/lib/rancher/k3s/server/node-token

# On the Worker node
curl -sfL https://get.k3s.io | K3S_URL=https://MASTER_IP:6443   K3S_TOKEN=TOKEN sh -

Deploying an Application

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: nginx:alpine
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 80
  type: LoadBalancer
kubectl apply -f deployment.yaml
kubectl get pods
kubectl get services

Helm: The K8s Package Manager

# Install Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# Install an application from a Helm Chart (e.g., cert-manager)
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager   --namespace cert-manager   --create-namespace   --set installCRDs=true

Ingress + Auto-SSL

# Traefik (included with k3s by default)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
  - hosts:
    - app.yourdomain.com
    secretName: webapp-tls
  rules:
  - host: app.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp-service
            port:
              number: 80
Recommendations: For a single VPS with 4-8 GB of RAM, k3s is an excellent choice. For a highly available production cluster, use at least 3 Master nodes (an odd number for etcd quorum).

Real Errors and Pod Statuses Explained

Three pod statuses cover almost every question a Kubernetes newcomer has. Let's look at what each one means and how to find the cause with kubectl describe.

The Pod Is Stuck in Pending

kubectl describe pod webapp-7d9f8c-abcde

Events:
  Warning  FailedScheduling  12s   default-scheduler  0/1 nodes are available: 1 Insufficient memory.

The scheduler cannot find a node with enough of the requested resources from resources.requests. The fix: lower requests.memory in the manifest, add a worker node, or check whether resources are already taken by other pods with kubectl describe node.

CrashLoopBackOff

kubectl get pods

NAME                      READY   STATUS             RESTARTS   AGE
webapp-7d9f8c-abcde        0/1    CrashLoopBackOff   6          4m

The container starts and immediately dies, and Kubernetes restarts it with an exponential backoff. The cause is almost always the application itself: check kubectl logs webapp-7d9f8c-abcde --previous — the --previous flag is essential, otherwise you see the logs of the new crashed process instead of the one before it.

ImagePullBackOff

Events:
  Warning  Failed     8s   kubelet  Failed to pull image "myrepo/app:v2": rpc error: code = Unknown desc = Error response from daemon: pull access denied for myrepo/app, repository does not exist or may require 'docker login'

Either there's a typo in the image name or tag, or the image is private and the node has no configured imagePullSecrets. Create a secret with kubectl create secret docker-registry regcred --docker-server=... --docker-username=... --docker-password=... and reference it in spec.imagePullSecrets.

k3s vs. Other Lightweight Distributions

Criterionk3sMicroK8sFull kubeadm
Minimum RAM512 MB540 MB2 GB on the control plane
State databaseSQLite (or etcd for HA)dqliteetcd
One-command installYesYes (snap)No, several steps

When k3s Is Not a Good Fit

  • The project fits inside Docker Compose or Docker Swarm — the Kubernetes overhead does not pay off at a scale of 1-2 services.
  • The team is unfamiliar with Kubernetes concepts and has no time to learn them — it is better to start with Docker Swarm.
  • You need a strictly certified upstream Kubernetes build for compliance — k3s replaces some components (Traefik, servicelb, local-path-provisioner) with its own lightweight versions, and some audits will require disabling that.
  • A VPS with less than 1 GB of RAM — even minimal k3s eats a significant share of memory; a regular stack without orchestration is enough for a website.
  • You need GPU task scheduling with vendor-specific device plugins — you will have to configure those manually, since k3s has no ready-made integration.

Verifying the Result

# All nodes are in the Ready state
kubectl get nodes -o wide

# All system pods are running
kubectl get pods -A | grep -v Running

# An HTTP response through the Ingress
curl -I https://app.yourdomain.com

# The cert-manager certificate issuance status
kubectl get certificate -A
kubectl describe certificate webapp-tls

If the grep -v Running command returns even one line (other than the header), some system pod did not come up — investigate with kubectl describe pod in the relevant namespace. Before using the cluster in production, set up external monitoring — it will show a service outage even if Kubernetes itself still considers the pod alive.

Was this article helpful?
← Back to Knowledge Base Ask Support