Lightweight Kubernetes for VPS: Setting Up a 3-Node K3s Cluster on High-Performance NVMe

Deploying standard upstream Kubernetes (k8s) across cost-effective cloud VPS instances often introduces crippling resource overhead, where etcd quorum operations and control plane daemons consume over 2.5 GB of RAM per node before scheduling a single container. By consolidating lightweight Kubernetes distributions from initial staging environments on CpanelFree into a dedicated 3-node K3s cluster powered by enterprise NVMe storage, systems architects can achieve sub-millisecond Raft consensus latency and reduce master overhead by over 70%. Eliminating legacy cloud provider plugins and bloated in-tree binaries transforms affordable Linux virtual servers into a resilient, production-ready container platform capable of executing mission-critical microservices at line-rate speed.

What is the Architecture and Performance Advantage of a 3-Node K3s Cluster on NVMe VPS?

Direct Answer: A 3-node K3s cluster on NVMe VPS instances provides a high-availability Kubernetes control plane utilizing embedded etcd, stripped-down binaries (<100MB footprint), and native container runtime integration. Replacing legacy hypervisor disk emulation with direct NVMe PCI pass-through yields over 85,000 fsync IOPS, maintaining Raft consensus under 2ms even during heavy burst write workloads.

Operating containerized environments on virtual private servers has historically forced engineers to choose between two extremes: simplistic single-node Docker Compose deployments that lack self-healing failover, or massive upstream Kubernetes clusters that demand multi-gigabyte memory footprints and specialized cloud infrastructure. K3s bridges this divide by packaging the entire Kubernetes API server, controller manager, scheduler, and containerd runtime into a single optimized binary that requires fewer than 512MB of RAM per control plane node.

When running across a three-node topology backed by hardware-grade PCIe NVMe storage, K3s unlocks an optimal quorum balance for distributed state storage without the operational complexity of managing external etcd topologies. In this technical deep dive, we examine the low-level Linux kernel optimizations, etcd WAL fsync behavior, WireGuard mesh networking, and declarative production configs required to engineer an enterprise-grade k3s 3 node cluster setup vps infrastructure.

High-Performance K3s vs. Standard Upstream Kubernetes Benchmark

The primary performance bottleneck in any distributed Kubernetes control plane is disk synchronization latency during etcd state updates. Upstream Kubernetes relies on verbose API schemas and unoptimized serialization, requiring aggressive write-ahead logging (WAL) that saturates standard virtualized disks. K3s optimizes internal datastore interactions and consolidates control loops into a single process, slashing both memory allocation and filesystem write overhead.

Feature / Metric Standard / Default Tuned / Production
Control Plane Memory / Node 2,450 MB (Upstream k8s) 512 MB (K3s Embedded)
etcd WAL fsync Latency 14.8 ms (Network/SATA) 0.78 ms (Enterprise NVMe)
Cluster Cold Boot Time 95 seconds 18 seconds
Overlay Network Throughput 6.2 Gbps (VXLAN encapsulation) 9.4 Gbps (WireGuard native)
Maximum API Server QPS 350 req/sec 2,200 req/sec

Architecture Note: In distributed consensus algorithms like Raft, the quorum formula is defined as (N / 2) + 1. A 3-node cluster maintains a strict quorum threshold of 2 nodes, allowing the cluster to survive the total failure of any single VPS without downtime or split-brain partitions. Adding a 4th node does not increase fault tolerance (it still tolerates only 1 failure), but increases network synchronization overhead. Therefore, a 3-node topology represents the exact gold standard for resilient, cost-effective high availability.

Why Enterprise NVMe Storage is Mandatory for etcd Stability

The Raft distributed consensus engine underlying etcd is extraordinarily sensitive to storage latency. Every write to the Kubernetes datastore—such as updating a ConfigMap, rescheduling a Pod, or rotating a service account secret—requires appending the transaction to the write-ahead log (WAL) and executing a synchronous fdatasync() call to physical non-volatile storage.

When hosting Kubernetes on budget cloud servers that rely on virtualized SATA disks or network-attached block storage (SAN/NFS), storage controller queue delays frequently push fdatasync() durations past 10 milliseconds. In etcd, when write latency exceeds the election timeout heartbeat interval (typically 1,000ms), follower nodes fail to receive consensus affirmations from the current Raft leader. This triggers cascading leader elections, cluster-wide freeze cycles, and severe API server 504 Gateway Timeout errors.

By provisioning nodes on pure enterprise-grade NVMe drives backed by direct PCIe Gen4 lanes, sequential write latencies drop consistently below 800 microseconds. Even under heavy cluster churn—such as continuous deployment rollouts or auto-scaling daemonsets—etcd commit times remain virtually unnoticeable, preventing control plane degradation.

Linux Kernel Tuning for Kubernetes NVMe Nodes

Before launching K3s binaries, the host Linux operating system must be tuned to eliminate virtual memory swapping, increase connection tracking capacity for container network interfaces (CNI), and optimize kernel inotify watchers for kubelet event tracking.

Create a dedicated sysctl configuration file at /etc/sysctl.d/99-kubernetes-nvme.conf on all three VPS nodes:

# /etc/sysctl.d/99-kubernetes-nvme.conf
# Enterprise Linux Kernel Tuning for High-Throughput K3s NVMe VPS Nodes

# 1. Memory Management & Swappiness Optimization
vm.swappiness = 0
vm.overcommit_memory = 1
vm.panic_on_oom = 0
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
vm.vfs_cache_pressure = 50

# 2. File Handles & Inotify Watchers for Kubelet / Pod Tracking
fs.file-max = 2097152
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 1048576

# 3. Network Connection Tracking & Packet Forwarding
net.ipv4.ip_forward = 1
net.ipv4.conf.all.forwarding = 1
net.ipv6.conf.all.forwarding = 1
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 86400
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 3600

# 4. TCP Socket Buffer Tuning for Low-Latency Inter-Node Traffic
net.core.somaxconn = 32768
net.core.netdev_max_backlog = 16384
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# 5. NVMe I/O Scheduler Concurrency
fs.aio-max-nr = 1048576

Apply these parameters immediately without rebooting by executing:

sudo sysctl --system

Additionally, configure process and open file limits inside /etc/security/limits.d/99-kubernetes.conf:

# /etc/security/limits.d/99-kubernetes.conf
root        soft    nofile      1048576
root        hard    nofile      1048576
root        soft    nproc       unlimited
root        hard    nproc       unlimited
*           soft    nofile      1048576
*           hard    nofile      1048576

Step-by-Step 3-Node K3s Cluster Deployment with Embedded etcd

In this deployment model, all three nodes participate as both control plane servers (running the API server and embedded etcd) and worker nodes (running application workloads). This ensures true high availability without dedicating empty nodes solely to management daemons.

1. Network Prerequisites & Topology Setup

Assign private mesh IP addresses to all three nodes. For optimal security and packet efficiency, ensure nodes can communicate over a private network interface (e.g., 10.10.0.0/24):

  • Node 1 (k3s-master-01): 10.10.0.11 (Public IP: 203.0.113.11)
  • Node 2 (k3s-master-02): 10.10.0.12 (Public IP: 203.0.113.12)
  • Node 3 (k3s-master-03): 10.10.0.13 (Public IP: 203.0.113.13)

Open required firewall ports between the nodes using ufw or native nftables:

# Allow inter-node Kubernetes API, etcd, Kubelet, and Flannel traffic
sudo ufw allow from 10.10.0.0/24 to any port 6443 proto tcp comment 'k3s-api'
sudo ufw allow from 10.10.0.0/24 to any port 2379:2380 proto tcp comment 'etcd-ha'
sudo ufw allow from 10.10.0.0/24 to any port 10250 proto tcp comment 'kubelet-metrics'
sudo ufw allow from 10.10.0.0/24 to any port 51820 proto udp comment 'flannel-wireguard'
sudo ufw allow 80,443/tcp comment 'http-https-ingress'
sudo ufw reload

2. Initializing Node 1 (Bootstrap Server)

Create the declarative K3s configuration directory and file at /etc/rancher/k3s/config.yaml on Node 1. We enable embedded etcd initialization, configure the Flannel CNI to use high-throughput native WireGuard encryption, and disable Traefik and servicelb if we prefer a custom Ingress controller (such as NGINX Ingress or Envoy):

# /etc/rancher/k3s/config.yaml (Node 1 - Bootstrap Master)
cluster-init: true
token: "K3sProductionSecuritySecretClusterToken2026SecureHash99"
node-ip: "10.10.0.11"
node-external-ip: "203.0.113.11"
bind-address: "10.10.0.11"
advertise-address: "10.10.0.11"
tls-san:
  - "10.10.0.11"
  - "203.0.113.11"
  - "k8s-cluster.internal.domain"

# Networking: WireGuard Native In-Kernel Mesh Encryption
flannel-backend: "wireguard-native"
flannel-iface: "eth1"

# Production Hardening: Disable Default Extras
disable:
  - traefik
  - servicelb
  - local-storage

# Kubelet Tuning for Production NVMe IOPS
kubelet-arg:
  - "max-pods=110"
  - "image-gc-high-threshold=85"
  - "image-gc-low-threshold=75"
  - "kube-api-qps=50"
  - "kube-api-burst=100"

# Embedded etcd Snapshot Schedule
etcd-snapshot-schedule-cron: "0 */6 * * *"
etcd-snapshot-retention: 28

Now install and launch K3s on Node 1 using the official installer:

curl -sfL https://get.k3s.io | sh -s - server

Verify that the bootstrap node is running and the single-node etcd cluster is established:

sudo k3s kubectl get nodes -o wide
sudo k3s etcdctl endpoint status --write-out=table

3. Joining Nodes 2 and 3 into the Embedded etcd Cluster

On Node 2 (10.10.0.12), create /etc/rancher/k3s/config.yaml pointing to Node 1 as the server join endpoint:

# /etc/rancher/k3s/config.yaml (Node 2)
server: "https://10.10.0.11:6443"
token: "K3sProductionSecuritySecretClusterToken2026SecureHash99"
node-ip: "10.10.0.12"
node-external-ip: "203.0.113.12"
bind-address: "10.10.0.12"
advertise-address: "10.10.0.12"
tls-san:
  - "10.10.0.12"
  - "203.0.113.12"

flannel-backend: "wireguard-native"
flannel-iface: "eth1"

disable:
  - traefik
  - servicelb
  - local-storage

kubelet-arg:
  - "max-pods=110"
  - "image-gc-high-threshold=85"
  - "image-gc-low-threshold=75"

Install and launch K3s on Node 2:

curl -sfL https://get.k3s.io | sh -s - server

Repeat the exact same process on Node 3 (10.10.0.13), setting node-ip to 10.10.0.13 and pointing server to either https://10.10.0.11:6443 or https://10.10.0.12:6443. Once installed, the 3-node HA quorum is fully operational.

Production Best Practice: Never register join nodes through a single point of failure DNS record. In production, configure an internal keepalived VIP (Virtual IP) or a round-robin DNS entry across all three private IPs so that node restarts automatically discover remaining active control plane members.

Verifying Cluster Health and Raft Quorum Status

To confirm that your k3s 3 node cluster setup vps is operating with zero errors, verify both Kubernetes node ready states and etcd cluster membership:

# Check node status across the cluster
sudo k3s kubectl get nodes -o wide

# Expected output:
# NAME            STATUS   ROLES                       AGE     VERSION        INTERNAL-IP
# k3s-master-01   Ready    control-plane,etcd,master   25m     v1.31.2+k3s1   10.10.0.11
# k3s-master-02   Ready    control-plane,etcd,master   18m     v1.31.2+k3s1   10.10.0.12
# k3s-master-03   Ready    control-plane,etcd,master   12m     v1.31.2+k3s1   10.10.0.13

Execute an etcd health and latency check across all three endpoints:

# Query embedded etcd members and active Raft leader
sudo k3s etcdctl endpoint status   --endpoints=https://10.10.0.11:2379,https://10.10.0.12:2379,https://10.10.0.13:2379   --cacert=/var/lib/rancher/k3s/server/tls/etcd/server-ca.crt   --cert=/var/lib/rancher/k3s/server/tls/etcd/client.crt   --key=/var/lib/rancher/k3s/server/tls/etcd/client.key   --write-out=table

The resulting table should confirm identical Raft terms, matching revision IDs within single-digit offsets, and single-millisecond round-trip response latencies.

Configuring Persistent Local NVMe Storage for Stateful Pods

Kubernetes stateful workloads (like PostgreSQL, MariaDB, Redis, or RabbitMQ) require lightning-fast storage. While distributed network storage solutions like Longhorn or Ceph offer multi-node volume replication, they introduce significant CPU and network serialization overhead that degrades database performance.

For maximum throughput on VPS nodes equipped with high-performance NVMe storage, the recommended pattern is combining the Kubernetes Local Path Provisioner with node affinity. Install the tuned Local Path Provisioner pointing to your NVMe mount point (/mnt/nvme-storage/k3s-volumes):

# Apply the official Rancher Local Path Provisioner manifest
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml

# Set local-path as the default StorageClass
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

With this configuration, PersistentVolumeClaims automatically bind to isolated NVMe directory trees, delivering pure bare-metal disk throughput with zero virtualization abstraction penalty.

When running mission-critical enterprise applications, eCommerce platforms, or high-traffic API backends that require guaranteed hardware isolation, dedicated CPU cores, and sustained 100% NVMe IOPS without hypervisor noisy neighbors, migrating your production clusters to MeraHost Enterprise Cloud guarantees enterprise-grade performance, carrier-neutral network peering, and fixed renewal pricing that never escalates over time.

Actionable FAQs: 3-Node K3s Cluster Administration

Why is a 3-node cluster superior to a 2-node cluster for K3s high availability?

A 2-node cluster cannot establish high availability because distributed consensus requires a strict majority quorum (N / 2) + 1. With two nodes, the majority threshold is 2; if either node fails, the remaining node represents only 50% of the cluster, causing etcd to freeze into read-only mode to prevent split-brain state divergence. A 3-node cluster has a majority threshold of 2, allowing it to seamlessly maintain full read/write operations when any single node fails.

Can I run application pods on the control plane nodes in a 3-node setup?

Yes. By default, K3s does not taint control plane nodes with node-role.kubernetes.io/master:NoSchedule. In a 3-node setup, all nodes act as both control plane members and worker executors. To protect etcd and the API server from memory exhaustion caused by rogue user pods, implement Kubernetes resource quotas (requests and limits) on all application manifests.

Why use Flannel WireGuard-Native over VXLAN in cloud VPS environments?

Standard VXLAN encapsulates pod network packets inside UDP packets, adding packet header overhead and transmitting pod-to-pod payloads in cleartext across the cloud provider’s network. Using wireguard-native delegates encryption and routing directly to the Linux kernel WireGuard subsystem, providing end-to-end cryptographic mutual authentication and WireGuard kernel efficiency with significantly lower CPU utilization than userland IPSec or VXLAN tunnels.

How often should embedded etcd snapshots be scheduled and backed up?

In production environments, configure automated snapshots every 6 hours with at least a 28-snapshot retention policy (representing 7 days of historical restore points). Snapshots are stored in /var/lib/rancher/k3s/server/db/snapshots/. You should configure K3s native S3 snapshot offloading to automatically sync these encrypted archives to off-site object storage for disaster recovery.

Deploy Enterprise-Grade Production Infrastructure

Need guaranteed performance with zero price hikes? Host mission-critical workloads on MeraHost with pure Enterprise NVMe, LiteSpeed Web Server, and Same Renewal Price, Always (starting at ₹99/mo).

🚀 Production Cloud Infrastructure

Deploy on High-Performance Cloud VPS with MeraHost

Need dedicated vCPU, root SSH access, pure NVMe SSD speeds, and 99.95% uptime SLA for your production stack? Launch with MeraHost — guaranteed price lock with zero renewal hikes.

Root SSH Access Dedicated vCPU & RAM Pure NVMe Storage 99.95% Uptime SLA
⚡ Instant Provisioning · 🔒 Price Lock Guarantee · 🌐 Global Datacenters

Leave a Comment