VPS Tutorials
Deploy Lightweight Kubernetes with K3s on a VPS (2026): Single-Node Install, Traefik, HTTPS, Persistence and Backups
A complete tutorial for installing lightweight Kubernetes with K3s on a VPS: from choosing a configuration, single-node installation, Traefik domains and HTTPS, to local-path persistence, SQLite backups, multi-node scaling and common troubleshooting.

Deploy Lightweight Kubernetes with K3s on a VPS (2026): Single-Node Install, Traefik, HTTPS, Persistence and Backups
If you want to learn Kubernetes on a VPS, run personal projects, or build a lightweight cluster for a small team, K3s is usually less hassle than installing standard Kubernetes from scratch. It bundles containerd, Flannel, CoreDNS, Traefik, ServiceLB and local storage, so a single Server node install gives you a Kubernetes cluster that can run workloads.
But "lightweight" does not mean "no operations needed." A single node is not high availability, and local-path will not automatically replicate data to other machines. This article walks through everything from choosing a VPS to installation, application publishing, domain HTTPS, persistence, backups, scaling and troubleshooting.
| Option | Suitable scenarios | Main cost |
|---|---|---|
| Docker Compose | Deploying a few services on one machine, prioritizing simplicity | Lacks Kubernetes scheduling and ecosystem |
| Single-node K3s | Learning K8s, small applications, CI test environments | Node failure means total outage |
| Multi-node K3s | Needing scaling, rolling releases or node redundancy | More complex networking, storage and backups |
| Standard Kubernetes | Large teams and a full cloud-native platform | Highest installation and maintenance cost |
If you only need to run a blog, a database and a few containers, the VPS Docker Compose production configuration guide is usually more direct. Choose K3s when you need Helm, Ingress, Operators, rolling updates or want to learn the Kubernetes API.
K3s officially recommends a minimum of 1 CPU core and 512MB of memory, recommends at least 2 cores and 1GB of memory, and suggests using an SSD. However, that number is only enough for the cluster itself and extremely light workloads; for actual purchases, start from the following:
| Purpose | Suggested configuration |
|---|---|
| Learning and temporary testing | 2 cores, 2GB, 30GB SSD |
| Personal website and lightweight API | 2–4 cores, 4GB, 60GB NVMe |
| Databases, monitoring, multiple applications | 4 cores, 8GB+, 100GB NVMe |
If you are unsure about the configuration, first read How to choose a VPS configuration. This article uses Ubuntu 24.04 and a KVM VPS with a public IPv4 address as an example. Before installing, confirm:
- The domain's A record already points to the VPS;
- TCP 80 and 443 are open to the public internet;
- A single node does not need to expose 6443 to the public internet; for remote management, prefer a VPN or restrict source IPs;
- With multiple nodes, only allow nodes to access TCP 6443, UDP 8472 and TCP 10250 over the private network;
- UDP 8472 is the Flannel VXLAN port and must never be open to the entire internet.
First update the system and check whether 80/443 are occupied:
sudo apt update
sudo apt upgrade -y
sudo ss -lntup | grep -E ':(80|443|6443)\b' || true
If Nginx, Caddy or Docker containers already occupy 80/443, the default Traefik will fail to bind the ports. First use the VPS port occupation troubleshooting guide to identify the process; do not directly kill unknown services.
Use the official installation script:
curl -sfL https://get.k3s.io | sh -
The installer creates and enables the k3s service, and also installs kubectl, crictl, ctr, a stop script and an uninstall script. Check the status:
sudo systemctl status k3s --no-pager
sudo k3s kubectl get nodes -o wide
sudo k3s kubectl get pods -A
The node should be Ready, and Pods in kube-system such as CoreDNS, Traefik, metrics-server and local-path-provisioner should gradually enter Running or Completed.
The administrator kubeconfig is located at /etc/rancher/k3s/k3s.yaml, with default permissions of 600. The safest approach is to keep using:
sudo k3s kubectl get nodes
If you need to run kubectl under your personal account, you can copy the kubeconfig and tighten permissions:
mkdir -p "$HOME/.kube"
sudo cp /etc/rancher/k3s/k3s.yaml "$HOME/.kube/config"
sudo chown "$(id -u):$(id -g)" "$HOME/.kube/config"
chmod 600 "$HOME/.kube/config"
kubectl get nodes
Do not send the kubeconfig to chat tools or commit it to Git; it contains cluster administrator credentials.
Create whoami.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami:latest
ports:
- containerPort: 80
resources:
requests:
cpu: 20m
memory: 32Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
selector:
app: whoami
ports:
- port: 80
targetPort: 80
Apply and check:
kubectl apply -f whoami.yaml
kubectl rollout status deployment/whoami
kubectl get pods,svc
In production, do not use a floating latest image long-term; pin a verified version or digest.
K3s deploys Traefik by default, and ServiceLB uses the host's 80/443 ports. First create an HTTP Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: whoami
spec:
ingressClassName: traefik
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: whoami
port:
number: 80
kubectl apply -f ingress.yaml
kubectl get ingress
curl -I http://app.example.com
Traefik handles HTTPS termination, but you still need to provide the certificate. If you already have a certificate, you can create a TLS Secret:
kubectl create secret tls app-example-tls \
--cert=fullchain.pem \
--key=privkey.pem
Then add it to the Ingress's spec:
tls:
- hosts:
- app.example.com
secretName: app-example-tls
If you need automatic issuance and renewal, you can install cert-manager and then create an ACME Issuer/ClusterIssuer. Do not manually modify /var/lib/rancher/k3s/server/manifests/traefik.yaml; K3s restores the default content on startup. For Traefik customization, use an additional HelmChartConfig.
If you are more familiar with a standalone reverse proxy, you can also disable Traefik during installation and refer to the Caddy automatic HTTPS guide, but you must explicitly set --disable=traefik in the first installation configuration to avoid port conflicts.
K3s includes the Local Path Provisioner, so you can create PVCs directly:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 5Gi
kubectl apply -f pvc.yaml
kubectl get pvc,pv
local-path uses the local disk of the node where the Pod runs. After a Pod is recreated, data is usually still there, but if the node is permanently damaged, the data will not automatically appear on another node. Therefore:
- A single node can be used for lightweight applications, but off-machine backups are mandatory;
- For multi-node databases, do not treat local-path as shared or replicated storage;
- When cross-node persistence is needed, evaluate Longhorn, cloud disk CSI or an externally managed database;
- Before scaling, confirm the PVC's access mode, topology and recovery process.
By default, a single Server node uses SQLite. The official requirements are to back up:
/var/lib/rancher/k3s/server/db/;/var/lib/rancher/k3s/server/token;- Secure copies of application manifests, Helm values and Secrets;
- Business data such as local-path, databases and user-uploaded files.
To obtain a consistent copy, you can stop K3s during a maintenance window and then copy the database and token:
sudo systemctl stop k3s
sudo tar -C /var/lib/rancher/k3s/server \
-czf /root/k3s-control-plane-$(date +%F).tar.gz db token
sudo systemctl start k3s
sudo k3s kubectl get nodes
A control-plane backup is not the same as a business data backup; PVC contents must be backed up separately to another machine or object storage. For a complete strategy, refer to the VPS backup and recovery drill guide.
Only when using embedded etcd should you use the k3s etcd-snapshot process; for the default single-node SQLite, do not copy etcd snapshot commands. Regardless of the datastore, recovery requires the original server token, otherwise encrypted data may be unusable.
On the Server, read the join token:
sudo cat /var/lib/rancher/k3s/server/node-token
Run on the new VPS:
curl -sfL https://get.k3s.io | \
K3S_URL=https://SERVER_PRIVATE_IP:6443 \
K3S_TOKEN='替换为真实Token' sh -
Each machine must use a unique hostname. Then check on the Server:
sudo k3s kubectl get nodes -o wide
Adding an Agent only adds compute capacity; it does not make the control plane highly available. True HA requires multiple Server nodes with embedded etcd or an external database, and also requires redesigning the load entry point and persistence.
Back up before upgrading, and record the original installation parameters. Officially, you can upgrade by re-running the installation script with the same configuration; if you forget the original environment variables or parameters, you may lose configuration. Stable businesses should also follow the Kubernetes version skew policy and not skip intermediate minor versions.
Common troubleshooting commands:
kubectl get nodes
kubectl get pods -A -o wide
kubectl describe pod POD_NAME
kubectl logs POD_NAME --previous
sudo journalctl -u k3s -n 200 --no-pager
kubectl top nodes
kubectl top pods -A
Pending: check CPU/memory requests, PVCs, taints and scheduling events;CrashLoopBackOff: check current logs,--previouslogs, environment variables and health checks;- Ingress access failure: check DNS, 80/443, security groups, Traefik Pods and the Ingress backend;
- Node
NotReady: checkk3s/k3s-agentlogs, 6443 connectivity, time synchronization and Flannel firewall rules; - Empty metrics: confirm metrics-server is healthy and allow TCP 10250 between nodes.
For long-term operation, you can combine with the Prometheus + Grafana monitoring solution to observe nodes, Pods, disks and certificate expiration times.
The uninstall command for a Server node is:
sudo /usr/local/bin/k3s-uninstall.sh
It stops K3s and deletes the local cluster datastore, Local Storage PV data, node configuration and related scripts. Before running it, you must confirm that both the control plane and business data have been verified recoverable on another machine; this is not an ordinary "stop service" command.
Yes, for learning and extremely light workloads, but you need to limit Pod resources and monitor memory. To run a database, monitoring and multiple applications at the same time, start from 4GB or 8GB.
No. It has only one control plane and one failure domain; when the VPS goes down, the entire cluster is unavailable.
No. The default Traefik provides 80/443 entry points, but certificates must be configured through a TLS Secret, cert-manager or another certificate process.
No. Agents add compute capacity, and local-path is still node-local storage. Databases and uploaded files need an independent cross-node storage or backup solution.
First set requests/limits for real workloads, then complete off-machine backups and recovery drills, and only then consider multiple nodes. Public ports should follow the principle of least exposure; if you change the firewall, first read the UFW firewall self-rescue checklist to avoid locking yourself out of the server.