Kubernetes on cloud VM instances carries a hidden cost most teams never fully account for: the underlying VM is already a virtualized slice of a physical host, so every container running inside it is virtualized twice over, and cross-node network traffic between cloud VM instances is subject to the same noisy-neighbor jitter as any other cloud workload. Running Docker and Kubernetes directly on dedicated bare-metal servers removes a full layer of virtualization overhead and gives you predictable, low-latency networking between nodes — which matters enormously for stateful workloads, databases running inside the cluster, and any service sensitive to inter-pod network latency. This guide covers real node sizing math, resource request/limit planning, storage classes, and cluster networking for bare-metal Kubernetes.

Bare Metal Kubernetes vs Managed Cloud Kubernetes: The Real Trade-offs

FactorBare Metal on Dedicated ServersManaged Cloud Kubernetes (EKS/GKE/AKS)
Raw compute performance per dollarHigher — no hypervisor tax, full CPU/RAM/NVMe accessLower — cloud VM virtualization overhead plus provider margin
Operational burdenYou own control-plane upgrades, etcd health, node OS patchingControl plane is managed for you; you focus on workloads
Network latency between nodesConsistently low with a private network/VLAN between dedicated serversGenerally good but subject to cloud provider's underlying network conditions
Storage performance for stateful workloadsDirect NVMe access, no cloud block-storage abstraction layer latencyCloud block storage (EBS-class) typically has higher latency than local NVMe
Scaling elasticityRequires provisioning new dedicated nodes in advance (hours, not seconds)Near-instant autoscaling of nodes and pods

The practical takeaway: dedicated bare-metal Kubernetes wins decisively on cost-per-compute and storage/network performance for steady-state workloads, while managed cloud Kubernetes wins on elastic burst scaling. Many teams run a bare-metal dedicated cluster for baseline steady-state load and burst into cloud capacity only for genuine spikes — though for most small-to-mid workloads, a well-sized dedicated cluster alone is simpler and cheaper.

Choosing a Kubernetes Distribution for Bare Metal

DistributionFootprintBest Fit
kubeadm (vanilla upstream Kubernetes)Full-featured, more manual setupTeams wanting full upstream compatibility and control over every component
K3sLightweight, single binary, strips some default componentsSmall-to-mid clusters, edge deployments, teams wanting fast setup with lower resource overhead
RKE2 (Rancher Kubernetes Engine)Security-hardened, CIS-benchmark-focused defaultsTeams with compliance requirements wanting hardened defaults out of the box
Talos LinuxImmutable, API-managed OS purpose-built for KubernetesTeams wanting a minimal-attack-surface, declarative-only node OS

For most self-managed bare-metal clusters on dedicated servers, K3s has become a popular default for small-to-mid clusters because of its minimal resource footprint (control plane components use meaningfully less RAM than vanilla Kubernetes), while kubeadm remains the choice for teams wanting exact upstream parity for compatibility with specific operators or Helm charts that assume a standard Kubernetes distribution.

Sizing Dedicated Nodes for a Kubernetes Cluster

Control Plane Nodes

Cluster SizeControl Plane NodesPer-Node CPU/RAM
Small (under 10 worker nodes)1 (non-HA) or 3 (HA)2-4 core, 8-16 GB
Medium (10-50 worker nodes)3 (HA required at this scale)4-8 core, 16-32 GB
Large (50+ worker nodes)3-5, with etcd potentially on separate dedicated nodes8-16 core, 32-64 GB, fast NVMe for etcd specifically

etcd, Kubernetes' cluster state store, is extremely latency-sensitive to disk write performance — always place etcd's data directory on NVMe, never on network-attached or slow storage, since etcd write latency directly affects overall cluster API responsiveness and, at the extreme, cluster stability.

Worker Nodes

Workload ProfilePer-Node CPUPer-Node RAMStorageEst. Monthly Cost per Node
Small microservices fleet, light traffic4-8 core16-32 GB500 GB-1 TB NVMe$85-$160
Mid-size production workloads, moderate stateful services8-16 core32-64 GB1-2 TB NVMe$180-$320
Large-scale production, heavy stateful workloads (databases, message queues in-cluster)16-32 core64-128 GB2-4 TB NVMe RAID 10$350-$650

Resource Requests and Limits: The Math That Actually Matters

Kubernetes schedules pods based on their declared requests, not their actual usage — a node's "allocatable" capacity is what determines how many pods can be scheduled, and undersizing requests leads to overcommitment and noisy-neighbor problems between pods on the same node, exactly the problem bare metal was supposed to solve.

A Practical Sizing Example

A worker node with 32 GB RAM and 8 cores, after subtracting roughly 1-2 GB and 0.5-1 core for the OS and kubelet/container runtime overhead, has approximately 30 GB and 7 cores of allocatable capacity. If each application pod requests 512 MB RAM and 250m CPU, that node can schedule roughly 55-60 pods purely on RAM requests, but real-world limits are usually reached sooner due to CPU request accumulation and per-node pod-count caps (default 110 pods per node in most distributions).

Requests vs Limits

  • Requests — what the scheduler guarantees is reserved for the pod; set this close to actual typical usage, not a padded guess, so scheduling capacity isn't wasted.
  • Limits — the hard ceiling the pod cannot exceed; setting CPU limits too aggressively can cause CPU throttling even when the node has spare capacity, a frequently misunderstood Kubernetes behavior worth testing under real load before finalizing.

Storage Classes for Bare-Metal Kubernetes

Cloud Kubernetes gets dynamic block storage provisioning for free (EBS, Persistent Disk); bare-metal clusters need an explicit choice:

Storage SolutionApproachBest Fit
Local Path ProvisionerUses local node NVMe directly, no replicationSimple stateful workloads where the application handles its own replication (e.g., a database with its own clustering)
LonghornDistributed block storage replicated across nodesTeams wanting cloud-like dynamic PVC provisioning with built-in replication on bare metal
Rook/CephFull distributed storage cluster (block, object, and file)Larger clusters needing enterprise-grade distributed storage with strong consistency guarantees
NFS-backed storageCentralized NFS server providing shared volumesSimpler shared-storage needs where distributed replication isn't required

Local NVMe with an application-level replication strategy (e.g., a StatefulSet running a clustered database like PostgreSQL with Patroni, or a replicated message queue) frequently outperforms distributed storage layers like Longhorn or Ceph for latency-sensitive workloads, at the cost of needing the application itself to handle replication rather than relying on the storage layer.

Networking Between Dedicated Nodes

Private Network / VLAN

Cluster inter-node traffic (pod-to-pod, kubelet-to-API-server, etcd replication) should run over a private network between your dedicated servers, not the public internet — most dedicated server providers support a private VLAN between servers in the same data center at no or low additional cost, and this should be a non-negotiable requirement for any multi-node bare-metal cluster.

CNI Plugin Choice

CNI PluginCharacteristics
CiliumeBPF-based, strong network policy enforcement and observability, increasingly the modern default
CalicoMature, widely used, solid network policy support with either BGP or overlay networking modes
FlannelSimplest to set up, minimal feature set, good for smaller clusters without complex network policy needs

Backup and Disaster Recovery for a Bare-Metal Cluster

Managed cloud Kubernetes typically bundles etcd snapshotting and control-plane recovery tooling; a bare-metal cluster needs this built and tested deliberately.

etcd Snapshots

Schedule regular etcdctl snapshot save backups (or the equivalent built into your chosen distribution) to storage outside the cluster itself, since a snapshot stored only on the same nodes that might fail provides no real protection.

Application-Level Backups

For stateful workloads using local NVMe with application-level replication, back up at the application layer (database dumps, replicated storage snapshots) rather than relying solely on Kubernetes-level primitives, which capture pod and configuration state but not necessarily consistent application data snapshots.

Testing Recovery, Not Just Taking Backups

A snapshot that has never been restored in a drill is an unverified assumption, not a backup — periodically practice restoring etcd and critical stateful workloads onto a scratch cluster to confirm the recovery process actually works end to end before you need it during a real incident.

Node Failure vs Cluster Failure

Design for the far more common failure mode first: a single worker node going offline (hardware fault, network blip, OS crash) should be handled automatically by Kubernetes rescheduling affected pods elsewhere, assuming sufficient spare capacity exists on remaining nodes. A full cluster-level failure (all control-plane nodes down simultaneously) is rarer but should still have a documented, tested recovery runbook rather than being figured out for the first time mid-incident.

High Availability and Rack-Level Fault Tolerance

Bare-metal dedicated servers, unlike cloud VM instances, are tied to specific physical hardware in a specific rack — a consideration cloud users rarely think about but that matters directly for bare-metal cluster design.

  • Spread control-plane nodes across separate physical hosts at minimum — never run all three HA control-plane nodes as VMs on the same underlying physical dedicated server, which defeats the purpose of a 3-node HA quorum entirely.
  • Ask your provider about power and network redundancy per rack — a data center with redundant power feeds and network uplinks per rack reduces the chance that a single rack-level fault takes out multiple nodes simultaneously.
  • Consider spreading nodes across two facilities for critical workloads — at the cost of higher inter-node latency, running a subset of nodes in a second facility protects against a full single-facility outage, though this adds real complexity to etcd's latency-sensitive consensus protocol and should be tested carefully before relying on it.

Setting Up a Bare-Metal Kubernetes Cluster: Step by Step

1. Provision Dedicated Nodes and Private Networking

Order dedicated servers in the same data center with a private VLAN connecting them, and confirm private-network throughput and latency between nodes before proceeding.

2. Install a Container Runtime

Install containerd (the standard runtime for current Kubernetes versions, since dockershim was removed) on every node.

3. Bootstrap the Control Plane

Use kubeadm, K3s's built-in server mode, or RKE2 to initialize the control plane, generating the join tokens needed for worker nodes.

4. Join Worker Nodes

Run the generated join command on each worker node, confirming each joins successfully with kubectl get nodes.

5. Install a CNI Plugin

Deploy your chosen CNI (Cilium, Calico, or Flannel) so pod-to-pod networking functions across nodes.

6. Install a Storage Provisioner

Deploy Longhorn, Rook/Ceph, or configure local-path provisioning depending on your stateful workload requirements.

7. Set Up Ingress and Monitoring

Install an ingress controller (NGINX Ingress or Traefik are common defaults) for routing external traffic, and deploy Prometheus/Grafana for cluster and workload monitoring.

GitOps and Deployment Workflow

Bare-metal clusters benefit as much from GitOps discipline as cloud clusters do — arguably more, since there is no cloud console fallback to manually patch a misconfigured resource.

Declarative Deployment with Argo CD or Flux

Rather than applying manifests manually via kubectl apply, a GitOps controller (Argo CD or Flux) continuously reconciles the cluster's actual state against manifests stored in a Git repository, automatically correcting drift and providing a clear audit trail of every change that reached the cluster.

Helm for Reusable Application Packaging

Most production workloads and third-party components (ingress controllers, monitoring stacks, storage provisioners) are distributed as Helm charts; standardizing on Helm plus a values-per-environment convention keeps configuration consistent across your dedicated node fleet as it grows.

Observability for a Self-Managed Cluster

Cloud-managed Kubernetes often bundles basic monitoring; a bare-metal cluster needs this built deliberately.

LayerTypical ToolingWhat to Watch
Node-level metricsnode_exporter + PrometheusCPU/RAM/disk saturation per physical dedicated server, not just per pod
Cluster/workload metricskube-state-metrics + Prometheus + GrafanaPod restart counts, pending pod counts, resource request vs actual usage per workload
LogsLoki, or the ELK/OpenSearch stackCentralized application and control-plane logs across all nodes, since SSHing into individual dedicated servers to tail logs does not scale past a handful of nodes
etcd health specificallyetcd's built-in Prometheus metrics endpointDisk write latency (wal_fsync_duration_seconds) and leader election frequency — both early indicators of storage or network problems

Multi-Tenancy on a Shared Bare-Metal Cluster

Running multiple teams or applications on one bare-metal cluster requires deliberate isolation, since Kubernetes' default posture does not isolate namespaces from each other without explicit configuration.

  • Namespace-based separation with ResourceQuotas — set per-namespace CPU/memory quotas so one team's workload cannot consume the entire node fleet's capacity and starve others.
  • NetworkPolicies for pod-to-pod isolation — by default, any pod can reach any other pod across namespaces; explicit NetworkPolicy rules (supported by Cilium and Calico) are required to restrict cross-namespace or cross-team traffic.
  • Dedicated node pools for sensitive or noisy workloads — use taints and tolerations to reserve specific dedicated nodes for workloads with special requirements (GPU workloads, compliance-isolated data), preventing unrelated pods from being scheduled there.

Common Bare-Metal Kubernetes Issues

  • Pods pending with "insufficient CPU/memory" — check actual node allocatable capacity versus the sum of all scheduled pod requests; this is a scheduling math problem, not necessarily a real resource shortage.
  • etcd latency warnings in logs — almost always a disk latency issue; confirm etcd's data directory is on genuinely fast NVMe, not a slower shared volume.
  • Cross-node pod networking not working — verify the CNI plugin is correctly installed and that any required kernel modules (e.g., for BGP-mode Calico) and firewall rules for the CNI's overlay/VXLAN ports are in place.
  • CPU throttling despite apparent spare node capacity — check pod CPU limits; Kubernetes' CFS-based CPU throttling can throttle a pod that hits its limit even when the node has idle CPU cycles available.
  • Node NotReady after a reboot — confirm kubelet and container runtime services are configured to start automatically on boot, and that the private network interface used for cluster communication comes up before the kubelet service starts.
  • One team's workload starving another's on a shared cluster — apply ResourceQuotas and, if contention persists, move the noisy workload to a dedicated tainted node pool rather than continually raising shared-namespace quotas.
  • Configuration drift between what's deployed and what's in Git — adopt a GitOps controller so manual kubectl changes get reconciled back to the Git-defined state rather than silently diverging over time.

Docker/Kubernetes Dedicated Server Buyer's Checklist

  • Does the provider support a private network/VLAN between multiple dedicated servers at reasonable cost?
  • Is NVMe storage available and fast enough for etcd and any latency-sensitive stateful workloads?
  • Can you provision additional identical nodes quickly to scale the cluster horizontally?
  • Does the provider give full root/kernel-level access needed for CNI plugins and container runtime configuration?
  • Is there IPMI/KVM remote access for troubleshooting a node that becomes unreachable over the network?
  • What is the actual network bandwidth and latency between nodes if they are ever provisioned in different racks or facilities?

Frequently Asked Questions

Do I need Kubernetes, or is plain Docker enough for my workload?

For a handful of services on one or two servers, Docker Compose is simpler and sufficient. Kubernetes earns its operational complexity once you need multi-node scheduling, self-healing, rolling deployments across many services, or are already standardizing on Kubernetes-native tooling.

How many dedicated servers do I need for a production Kubernetes cluster?

A minimum viable production setup is typically 3 control-plane nodes (for HA quorum) plus 2 or more worker nodes; smaller non-critical clusters can run a single combined control-plane-and-worker node, accepting reduced fault tolerance.

Is bare-metal Kubernetes harder to operate than managed cloud Kubernetes?

Yes, meaningfully — you own control-plane upgrades, etcd health, and node OS patching that a managed service handles for you. The trade-off is lower cost and better raw performance per dollar, which is why many teams choose managed Kubernetes despite the premium until scale or cost justifies the operational investment in bare metal.

What CNI plugin should I use for a new bare-metal cluster?

Cilium is a strong modern default for most new clusters due to its eBPF-based performance and built-in observability; Calico remains a solid, mature alternative, particularly if your team already has Calico operational experience.

How much RAM overhead does Kubernetes itself use per node?

Budget roughly 1-2 GB for the OS, kubelet, and container runtime combined on a typical worker node, plus additional overhead for any DaemonSets you run cluster-wide (monitoring agents, log shippers, CNI components) — this reduces your actual allocatable capacity below the node's raw RAM figure.

Can I mix dedicated servers from different data centers in one cluster?

Technically possible, but strongly discouraged for latency-sensitive components like etcd — cross-facility network latency and reduced bandwidth compared to an intra-facility private VLAN can cause cluster instability; keep control-plane nodes within the same low-latency network whenever possible.

Is Longhorn or local storage better for my Kubernetes cluster?

Local NVMe storage with application-level replication offers the best raw performance for latency-sensitive stateful workloads. Longhorn or Rook/Ceph are better choices when you need dynamic PVC provisioning with storage-layer replication and your applications don't handle their own data replication.

How do I monitor a bare-metal Kubernetes cluster without a cloud provider's built-in dashboard?

Deploy Prometheus with node_exporter and kube-state-metrics for metrics, Grafana for dashboards, and a log aggregator like Loki — this combination replicates most of what managed cloud Kubernetes offerings provide out of the box, at the cost of setting it up and maintaining it yourself.

Can multiple teams safely share one bare-metal Kubernetes cluster?

Yes, with deliberate isolation — namespace-based ResourceQuotas prevent one team from consuming the whole cluster's capacity, and NetworkPolicies restrict which namespaces can reach each other, since Kubernetes does not isolate namespaces from each other by default.

What is the benefit of GitOps for a self-managed cluster?

A GitOps controller like Argo CD or Flux continuously reconciles the cluster against manifests stored in Git, which both provides an audit trail of every deployed change and automatically corrects manual configuration drift — particularly valuable on bare metal, where there is no cloud console to fall back on for quick fixes.

How do I achieve high availability across physical dedicated servers, not just virtual nodes?

Spread control-plane and critical worker nodes across genuinely separate physical dedicated servers, ideally in different racks with independent power and network paths, so a single hardware or rack-level fault cannot take down cluster quorum — this is a consideration that does not exist on cloud VM-based clusters, where the provider abstracts physical placement for you.

Should I back up etcd separately from my application data?

Yes — etcd snapshots capture cluster and configuration state, while stateful application data (databases, message queues) needs its own application-level backup strategy; relying on etcd snapshots alone will not restore your actual application data after a serious incident.

Bare-metal Kubernetes on properly sized dedicated hardware delivers performance and cost efficiency that virtualized cloud nodes cannot match for steady-state workloads. WebsNP's dedicated server plans support the private networking and NVMe storage a production cluster needs — compare against VPS options for smaller test clusters, or contact our team to plan node sizing and private networking for your cluster topology.