Prakhar Singh

Running Kubernetes on Bare Metal: A Step-by-Step Guide

Cover Image for Running Kubernetes on Bare Metal: A Step-by-Step Guide
Prakhar Singh
Prakhar Singh

Introduction


Kubernetes on bare metal means running Kubernetes directly on physical servers without a virtualization layer. In this setup, you have an “empty” server (no hypervisor or cloud VM), giving your containers direct access to hardware. This yields high performance and low latency, ideal for workloads like high-performance computing or large databases. It also offers full control over networking, storage, and compute resources.

In this guide, we’ll walk through setting up a bare-metal Kubernetes cluster step by step, using modern tools and best practices, on both Linux (Ubuntu or Fedora) and optional Windows worker nodes.

Prerequisites and Environment


Before starting, gather the following hardware and software prerequisites.

Hardware

  • • Control-plane node: 4+ CPU cores, 16+ GB RAM, 100+ GB disk
  • • Worker nodes: 2+ CPU cores, 8+ GB RAM
  • • Static IP addresses for all nodes
  • • Same network or properly routed subnets

Operating Systems

  • • Linux: Ubuntu 22.04+ or Fedora 38/39
  • • Windows (optional): Windows Server 2022+

Access and Networking

  • • Root or sudo access
  • • SSH enabled on Linux nodes
  • • Proper hostname resolution using /etc/hostname and /etc/hosts

Disable Swap (Linux)

bash
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab

Container Runtimes: Docker vs. containerd


Kubernetes requires a CRI-compatible container runtime. Popular options include Docker Engine, containerd, and CRI-O. Since Kubernetes v1.24, dockershim has been removed.

Ubuntu: Install containerd

bash
sudo apt update
sudo apt install -y containerd
bash
sudo containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl enable --now containerd

Fedora

bash
sudo dnf install -y containerd docker-cli
sudo systemctl enable --now containerd

Installing Kubernetes Components


Add Kubernetes Repository (Ubuntu)

bash
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
bash
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo systemctl enable --now kubelet

Verify:

bash
kubelet --version
kubeadm version
kubectl version --client

Initializing the Control Plane


Enable IP forwarding:

bash
sudo sysctl -w net.ipv4.ip_forward=1
echo 'net.ipv4.ip_forward=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Initialize the cluster:

bash
sudo kubeadm init --pod-network-cidr=10.244.0.0/16

Configure kubeconfig:

bash
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Installing a Pod Network Add-on


Flannel

bash
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Check:

bash
kubectl get pods -n kube-system

Joining Linux Worker Nodes


Run on each worker:

bash
sudo kubeadm join <master-ip>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>

Verify:

bash
kubectl get nodes

Adding a Windows Worker Node (Optional)


On Windows Server 2022 (PowerShell as Admin):

powershell
Invoke-WebRequest -Uri https://raw.githubusercontent.com/kubernetes-sigs/sig-windows-tools/master/hostprocess/Install-Containerd.ps1 -OutFile Install-Containerd.ps1
.\Install-Containerd.ps1 -ContainerDVersion 1.7.22

Invoke-WebRequest -Uri https://raw.githubusercontent.com/kubernetes-sigs/sig-windows-tools/master/hostprocess/PrepareNode.ps1 -OutFile PrepareNode.ps1
.\PrepareNode.ps1 -KubernetesVersion v1.33.2

Then run kubeadm join with the same token.

Verifying the Cluster and Deploying a Sample App


bash
kubectl get nodes
kubectl get pods -n kube-system

Deploy sample app:

bash
kubectl create deployment hello-web --image=nginx
kubectl expose deployment hello-web --port=80 --type=NodePort
kubectl get pods,svc

Tips and Next Steps


  • • Use MetalLB or a hardware load balancer
  • • Back up etcd regularly
  • • Install monitoring with Prometheus and Grafana
  • • Upgrade clusters using kubeadm upgrade

Running Kubernetes on bare metal gives unmatched performance and control, but also full responsibility. With proper planning and maintenance, it becomes a powerful production foundation.