Building a Lightweight Kubernetes Cluster (K3s) on a Single VPS

Estimated reading: 12 minutes 55 views

Kubernetes has become the standard platform for orchestrating containerized workloads, but standard distributions like upstream Kubernetes (k8s) require significant memory, CPU overhead, and administrative complexity. For small teams, staging environments, edge deployments, or single Virtual Private Servers (VPS), K3s provides a fully compliant, production-grade Kubernetes distribution engineered by Rancher Labs with minimal resource overhead.

K3s packages all Kubernetes control plane components—including the API server, controller manager, scheduler, CoreDNS, local-path storage provisioner, and Traefik ingress controller—into a single binary packaged with an embedded SQLite database. This design replaces heavyweight etcd clusters for single-node setups while drastically reducing memory consumption to under 512 MB of RAM for the control plane.

In this guide, you will configure a single Linux VPS running Ubuntu 24.04 LTS into a functional K3s cluster. You will prepare the host environment, deploy K3s using official installation methods, configure standard kubectl tooling, verify built-in networking and storage subsystems, deploy an authenticated cluster dashboard workload exposed through Traefik Ingress on port 80, and verify administrative operations through both command-line checks and an authenticated web interface.

Prerequisites

  • A clean Linux Virtual Machine running Ubuntu 24.04 LTS with at least 2 vCPUs and 4 GB of RAM.
  • Root administrative access to execute system configuration commands and service managers.
  • Outbound internet connectivity to retrieve official K3s binaries and container images.
  • TCP ports 80 (HTTP) and 6443 (Kubernetes API) available on the host network interface.

Key concepts

K3s Single-Binary Architecture

Traditional Kubernetes splits the control plane into separate processes (kube-apiserver, kube-scheduler, kube-controller-manager) and external etcd storage. K3s combines these components into a single multi-call binary. For single-node clusters, K3s replaces etcd with an embedded SQLite database through kine, reducing control plane memory usage by more than 75% without breaking the Kubernetes API specification.

Bundled Components (Traefik, CoreDNS, Local Path)

K3s includes pre-configured operational essentials out of the box: CoreDNS for in-cluster service discovery, Traefik as the default Ingress Controller listening on ports 80 and 443, and the Rancher Local Path Provisioner to dynamically satisfy PersistentVolumeClaims using host filesystem directories.

Kubeconfig and Access Model

The K3s installation creates an administrative kubeconfig file at /etc/rancher/k3s/k3s.yaml pointing to the local API endpoint (https://127.0.0.1:6443). Configuring standard user access and environment variables allows standard kubectl commands and external orchestrators to interact seamlessly with the cluster.

1. Prepare Host System and Install Dependencies

Before installing K3s, ensure the package cache is current and verify that the core transport utilities are present. The K3s installation script requires curl to retrieve binaries and ca-certificates to validate TLS certificates against public container registries and Rancher download endpoints. Updating the local apt cache guarantees reliable package resolution.

apt-get update && apt-get install -y curl ca-certificates

Observed result: Output from the successful recorded run:

Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
Get:2 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB]
Get:3 http://archive.ubuntu.com/ubuntu noble-updates InRelease [126 kB]
Get:4 http://archive.ubuntu.com/ubuntu noble-backports InRelease [126 kB]
Get:5 http://security.ubuntu.com/ubuntu noble-security/main amd64 Packages [1069 kB]
Get:6 http://archive.ubuntu.com/ubuntu noble/universe amd64 Packages [15.0 MB]
Get:7 http://security.ubuntu.com/ubuntu noble-security/main Translation-en [221 kB]
Get:8 http://security.ubuntu.com/ubuntu noble-security/main amd64 Components [46.5 kB]
Get:9 http://security.ubuntu.com/ubuntu noble-security/universe amd64 Packages [1216 kB]
Get:10 http://security.ubuntu.com/ubuntu noble-security/universe Translation-en [244 kB]
Get:11 http://security.ubuntu.com/ubuntu noble-security/universe amd64 Components [76.3 kB]
Get:12 http://security.ubuntu.com/ubuntu noble-security/universe amd64 c-n-f Metadata [24.2 kB]
Get:13 http://security.ubuntu.com/ubuntu noble-security/restricted amd64 Packages [1566 kB]
Get:14 http://security.ubuntu.com/ubuntu noble-security/restricted Translation-en [361 kB]

Step 1 · Prepare Host System and Install Dependencies

Expected result: Package lists are updated and curl and ca-certificates are installed and ready.

Note: Modern Ubuntu 24.04 cloud images generally include curl, but explicitly verifying dependencies prevents script termination.

Note: K3s handles its own containerd runtime, iptables setup, and cni plugins without requiring external Docker installations.

2. Install K3s Lightweight Kubernetes

Execute the official Rancher K3s installation script. yaml with universal read permissions, allowing non-root tools and service accounts to access cluster metrics and API endpoints without permission barriers.

The installer automatically downloads the unified binary, installs systemd service units, and starts the k3s service.

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--write-kubeconfig-mode=644" sh -

Observed result: Output from the successful recorded run:

[INFO]  Finding release for channel stable
[INFO]  Using v1.36.5+k3s1 as release
[INFO]  Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.36.5%2Bk3s1/sha256sum-amd64.txt
[INFO]  Downloading binary https://github.com/k3s-io/k3s/releases/download/v1.36.5%2Bk3s1/k3s
[INFO]  Verifying binary download
[INFO]  Installing k3s to /usr/local/bin/k3s
[INFO]  Skipping installation of SELinux RPM
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  Creating /usr/local/bin/ctr symlink to k3s
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
[INFO]  systemd: Starting k3s

Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.

Step 2 · Install K3s Lightweight Kubernetes

Expected result: The K3s installer downloads the release binary, creates the systemd service unit, and starts the active control plane.

Note: The official installation script is idempotent; re-running it upgrades K3s to the latest stable release while preserving existing workloads.

Note: K3s automatically installs symlinks for kubectl, crictl, and ctr in /usr/local/bin pointing directly to the k3s binary.

3. Configure Kubectl Access and Verify Node Readiness

Configure the standard kubectl environment by establishing a default ~/.kube/config configuration file populated from the K3s cluster credentials. Once the configuration is in place, poll the cluster node status until the single VPS registers as ‘Ready’, indicating that the Kubelet agent and containerd container runtime have successfully initialized.

mkdir -p ~/.kube && cp /etc/rancher/k3s/k3s.yaml ~/.kube/config && chmod 600 ~/.kube/config && kubectl wait --for=condition=Ready node --all --timeout=60s && kubectl get nodes -o wide

Observed result: Output from the successful recorded run:

node/k3s-vps condition met
NAME      STATUS   ROLES           AGE   VERSION        INTERNAL-IP       EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION              CONTAINER-RUNTIME
k3s-vps   Ready    control-plane   18s   v1.36.5+k3s1   192.168.250.215   <none>        Ubuntu 24.04.5 LTS   6.8.0-142-generic (amd64)   containerd://2.3.4-k3s1.36

Step 3 · Configure Kubectl Access and Verify Node Readiness

Expected result: The cluster node registers in ‘Ready’ status with control-plane and master roles assigned.

Note: K3s symlinks kubectl to k3s kubectl automatically, which falls back to /etc/rancher/k3s/k3s.yaml if ~/.kube/config is not present.

Note: Populating ~/.kube/config ensures compatibility with Helm, k9s, and third-party Kubernetes administration tools.

4. Run the verified updated command

Run the command shown below, then execute the verification checks for this step.

for dep in coredns local-path-provisioner traefik; do until kubectl get deployment "$dep" -n kube-system >/dev/null 2>&1; do sleep 2; done; kubectl rollout status deployment/"$dep" -n kube-system --timeout=180s; done && kubectl get pods -n kube-system

Observed result: Output from the successful recorded run:

deployment “coredns” successfully rolled out
deployment “local-path-provisioner” successfully rolled out
Waiting for deployment “traefik” rollout to finish: 0 of 1 updated replicas are available…
deployment “traefik” successfully rolled out
NAME                                      READY   STATUS      RESTARTS      AGE
coredns-7cfb7bc9c7-9vw2h                  1/1     Running     0             38s
helm-install-traefik-crd-n4w5r            0/1     Completed   0             39s
helm-install-traefik-pgqdw                0/1     Completed   2 (27s ago)   39s
local-path-provisioner-77b9867795-b44ln   1/1     Running     0             38s
metrics-server-6dc596dfb8-gp966           1/1     Running     0             38s
svclb-traefik-33c61ba2-75d8r              2/2     Running     0             15s
traefik-7c8544f77-lmbns                   1/1     Running     0             15s

Step 4 · Run the verified updated command

Expected result: Verification succeeds and includes: 1; 1

5. Deploy Cluster Web Dashboard with Traefik Ingress

Deploy a containerized cluster web application to validate workload scheduling, service networking, and ingress routing on K3s. ‘), a ClusterIP Service on port 3000, and a Traefik Ingress routing all inbound HTTP traffic on port 80 (‘/’) to the dashboard service.

cat <<'EOF' > /tmp/cluster-dashboard.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cluster-dashboard
namespace: default
labels:
app: cluster-dashboard
spec:
replicas: 1
selector:
matchLabels:
app: cluster-dashboard
template:
metadata:
labels:
app: cluster-dashboard
spec:
containers:
- name: grafana
image: grafana/grafana:10.4.1
ports:
- name: http
containerPort: 3000
env:
- name: GF_SECURITY_ADMIN_USER
value: "admin"
- name: GF_SECURITY_ADMIN_PASSWORD
value: "TutorialClusterAdmin2026!"
- name: GF_USERS_ALLOW_SIGN_UP
value: "false"
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
name: cluster-dashboard
namespace: default
spec:
type: ClusterIP
selector:
app: cluster-dashboard
ports:
- name: http
port: 3000
targetPort: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cluster-dashboard
namespace: default
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: cluster-dashboard
port:
number: 3000
EOF
kubectl apply -f /tmp/cluster-dashboard.yaml

Observed result: Output from the successful recorded run:

deployment.apps/cluster-dashboard created
service/cluster-dashboard created
ingress.networking.k8s.io/cluster-dashboard created

Step 5 · Deploy Cluster Web Dashboard with Traefik Ingress

Expected result: The cluster-dashboard Deployment, Service, and Ingress resources are created in the default namespace.

Note: Setting GF_SECURITY_ADMIN_PASSWORD avoids interactive first-run password prompts while establishing a known disposable password for verification.

Note: The Ingress resource utilizes standard Kubernetes networking.k8s.io/v1 specifications recognized natively by K3s’s built-in Traefik.

6. Wait for Workload Rollout and Ingress HTTP Readiness

Wait for the dashboard pod container image to download and transition into the Running state. Once the deployment rollout finishes, execute a readiness loop testing the local HTTP port 80 ingress endpoint. This verifies that Traefik has discovered the Ingress resource, configured its upstream routing table, and confirmed backend connectivity before browser verification.

kubectl rollout status deployment/cluster-dashboard -n default --timeout=180s && until curl -sf http://127.0.0.1/api/health | grep -q 'ok'; do sleep 2; done && echo 'Cluster dashboard is healthy and accessible via Traefik Ingress'

Observed result: Output from the successful recorded run:

deployment “cluster-dashboard” successfully rolled out
Cluster dashboard is healthy and accessible via Traefik Ingress

Step 6 · Wait for Workload Rollout and Ingress HTTP Readiness

Expected result: The deployment reaches ready status and Traefik begins serving healthy HTTP responses on port 80.

Note: Traefik dynamically watches the Kubernetes Ingress API; endpoint registration typically takes 2-5 seconds following pod startup.

Note: The /api/health endpoint returns an HTTP 200 with ‘{“commit”:…”database”:”ok”}’ when the service is fully initialized.

7. Authenticate and Verify Cluster Operations in Web Dashboard

Open the cluster web dashboard in your browser via the local Traefik ingress endpoint on port 80. ‘).

After authenticating, navigate through two distinct administrative sections: first open the Dashboards workspace to confirm visualization management, and then open the Administration panel to verify user and cluster security settings.

Open http://localhost/ in your browser.

  • Enter admin in input[name=’user’].
  • Enter the validated disposable credential in input[name=’password’].
  • Click Log in.
  • Click Dashboards.
  • Click Administration.

Step 7 · Authenticate and Verify Cluster Operations in Web Dashboard

Expected result: The web dashboard authenticates successfully, opens the Dashboards section, and navigates to the Administration settings page.

Note: Accessing the service on port 80 proves that K3s’s built-in Traefik ingress controller correctly handles path routing to downstream pods.

Note: Navigating between Dashboards and Administration proves an active, authenticated session with functional state persistence.

How the final setup works

On the single VPS, the K3s systemd service runs the unified control plane and Kubelet agent backed by containerd. CoreDNS provides cluster-internal name resolution, while Traefik binds directly to host ports 80 and 443 via hostPort/ServiceLB.

Workload pods run in their own network namespace via Flannel overlay networking. When an external client makes an HTTP request to port 80, Traefik inspects the Ingress resource and routes traffic directly to the ClusterIP service backing the application pod.

Troubleshooting

The K3s systemd service fails to start or reports cgroup configuration errors.

Likely cause: Ubuntu systemd cgroup v2 controllers may require standard Linux kernel compatibility or swap memory management settings.

Fix: Verify that systemd is active as PID 1 and inspect journalctl -u k3s.service -f. Ensure memory and CPU cgroup controllers are enabled in /sys/fs/cgroup.

kubectl commands return ‘The connection to the server 127.0.0.1:6443 was refused’.

Likely cause: The k3s service has not finished starting the embedded API server or the kubeconfig path is unreadable.

Fix: Run ‘systemctl status k3s’ to verify service health and ensure KUBECONFIG points to /etc/rancher/k3s/k3s.yaml with read permissions (mode 644).

Ingress routes return HTTP 404 or 502 Bad Gateway.

Likely cause: The Traefik ingress controller pod has not finished deploying or the upstream workload pod is still pulling its container image.

Fix: Run ‘kubectl rollout status deployment/traefik -n kube-system’ and verify backend pod readiness with ‘kubectl get pods -l app=cluster-dashboard’.

Useful commands

Check K3s Service Status

systemctl status k3s

View Cluster Nodes

kubectl get nodes -o wide

List All Cluster Pods

kubectl get pods -A

View Ingress Routes

kubectl get ingress -A

Stream K3s Logs

journalctl -u k3s -f -n 50

Conclusion

You have built and validated a lightweight, production-grade Kubernetes cluster (K3s) on a single Linux VPS. By leveraging K3s’s unified binary architecture, the cluster provides a complete Kubernetes control plane, container runtime, CoreDNS resolution, dynamic local storage, and Traefik ingress routing while maintaining a small memory footprint.

You verified the installation through kubectl commands, deployed an authenticated cluster web dashboard workload exposed through Traefik on port 80, and authenticated through the web UI to verify operational health.

  • Installed K3s on Ubuntu 24.04 using official methods with permissive kubeconfig access.
  • Verified control plane and node readiness using standard kubectl tooling.
  • Confirmed the health of bundled K3s services including CoreDNS, Local Path Provisioner, and Traefik Ingress.
  • Deployed a complete workload stack consisting of a Deployment, ClusterIP Service, and Ingress routing rules.
  • Verified end-to-end HTTP traffic routing and authenticated dashboard operations through the browser interface.
Share this Doc

Building a Lightweight Kubernetes Cluster (K3s) on a Single VPS

Or copy link

CONTENTS