Kubernetes is the standard platform for running containerized workloads across multiple servers with self-healing, rolling updates, and built-in service discovery. kubeadm is the upstream tool for bootstrapping a minimal cluster on plain Linux servers without managed cloud infrastructure.
This guide explains how to install Kubernetes on Ubuntu 26.04 using kubeadm, kubelet, and kubectl, with containerd as the container runtime. By the end you will have a control-plane node, one or more worker nodes, and a test workload confirmed running across the cluster.
Prerequisites: Two or more Ubuntu 26.04 servers with at least 2 GB RAM each (the control-plane node also needs 2 CPUs), sudo access, full network connectivity between nodes, and a stable IP on every machine. Run every command on all nodes unless the section says otherwise.
Install Kubernetes on Ubuntu: Prepare the System and Container Runtime
Disable swap. The kubelet refuses to start when swap is active under its default configuration. Disable it for the current session and remove the swap entries from /etc/fstab so it stays off after a reboot:
bash
sudo swapoff -a<br>sudo sed -i '/[[:space:]]swap[[:space:]]/ s/^[^#]/#&/' /etc/fstab<br>Load the required kernel modules. The overlay module supports OverlayFS, the default container filesystem driver. The br_netfilter module lets iptables see bridged network traffic, which Kubernetes needs for service routing and NetworkPolicies:
bash
sudo tee /etc/modules-load.d/k8s.conf <<EOF<br>overlay<br>br_netfilter<br>EOF<br>sudo modprobe overlay && sudo modprobe br_netfilter<br>Enable IP forwarding and bridge filtering:
bash
sudo tee /etc/sysctl.d/k8s.conf <<EOF<br>net.bridge.bridge-nf-call-iptables = 1<br>net.bridge.bridge-nf-call-ip6tables = 1<br>net.ipv4.ip_forward = 1<br>EOF<br>sudo sysctl --system<br>Install and configure containerd. Kubernetes communicates with container runtimes through the Container Runtime Interface (CRI). Ubuntu 26.04 provides containerd 2.x in its default repositories:
bash
sudo apt update && sudo apt install -y containerd<br>sudo mkdir -p /etc/containerd<br>containerd config default | sudo tee /etc/containerd/config.toml > /dev/null<br>sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml<br>sudo systemctl restart containerd && sudo systemctl enable containerd<br>Setting SystemdCgroup = true is not optional. The kubelet and the container runtime must use the same cgroup driver. Ubuntu uses systemd as its init system, so both must be set to the systemd cgroup driver. A mismatch causes pod failures that appear after the cluster is already running and are difficult to trace.
Add the Kubernetes Repository, Install Tools, and Initialize the Control Plane
Add the Kubernetes v1.36 apt repository. Kubernetes publishes a separate repository per minor version so an apt upgrade cannot accidentally move you across a minor version boundary:
bash
sudo apt install -y apt-transport-https ca-certificates curl gpg<br>sudo mkdir -p -m 755 /etc/apt/keyrings<br>curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg<br>echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list<br>sudo apt update<br>Install and hold the Kubernetes tools:
bash
sudo apt install -y kubelet kubeadm kubectl<br>sudo apt-mark hold kubelet kubeadm kubectl<br>The apt-mark hold step is critical. Kubernetes upgrades run through kubeadm's own upgrade procedure — a plain apt upgrade skips it and breaks the cluster. The kubelet restart loop you see immediately after install is expected; it stops once kubeadm init or kubeadm join provides the cluster configuration.
Initialize the control plane (control-plane node only). Choose a pod CIDR that does not overlap your host network. The example uses 10.244.0.0/16, which is Flannel’s default:
bash
sudo kubeadm init --pod-network-cidr=10.244.0.0/16<br>When the command finishes, it prints a kubeadm join command with a bootstrap token. Save this output. The token authenticates worker nodes during the join and expires after 24 hours.
Set up kubectl access for your regular user:
bash
mkdir -p $HOME/.kube<br>sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config<br>sudo chown $(id -u):$(id -g) $HOME/.kube/config<br>The copied admin.conf grants full cluster administrator access. Keep ~/.kube/config private.
Install a Pod Network, Join Worker Nodes, and Test the Cluster
Install Flannel (control-plane node only). Pods cannot communicate until a CNI network plugin is running. Running kubectl get nodes right after kubeadm init shows the control-plane node as NotReady for this reason:
bash
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml<br>kubectl get nodes<br>The node moves to Ready once the Flannel pods are running. If you prefer Calico or Cilium, install it instead and pass the matching CIDR to kubeadm init.
Join each worker node using the command saved from the init output. If the token has expired, generate a new one on the control-plane node:
bash
sudo kubeadm token create --print-join-command<br>Run the printed kubeadm join command on the worker. Verify it joined: kubectl get nodes.
bash
kubectl create deployment nginx-test --image=nginx<br>kubectl expose deployment nginx-test --port=80 --type=NodePort<br>kubectl rollout status deployment/nginx-test<br>kubectl get pods,svc<br>The NodePort service assigns a port in the 30000–32767 range. Open http://: to confirm the Nginx welcome page loads. Remove the test workload: kubectl delete service nginx-test && kubectl delete deployment nginx-test.
The cluster is ready. From here, deploy applications with Helm charts, add an ingress controller, and configure storage classes for persistent volumes. For a single-node lab, k3s or minikube are simpler starting points before committing to a kubeadm setup. Leave a comment below if you run into any issues.
PHP 8.5 is included in Ubuntu 26.04's default repositories and is the recommended version for…
Ubuntu 26.04 LTS "Resolute Raccoon" arrived on April 23, 2026 with Linux kernel 7.0, GNOME 50,…
Ubuntu 26.04 LTS "Resolute Raccoon" was released on April 23, 2026 with Linux kernel 7.0, GNOME desktop, and standard security support until April 2031. A clean install gives you a known-good starting point on new hardware, when replacing another operating system, or when an upgrade path is not practical. This guide walks through how to install Ubuntu 26.04: downloading and verifying the ISO, writing a bootable USB drive, completing the installer, and doing the initial setup after the first boot. Before you start: You need a USB drive with at least 12 GB of free space. Back up any existing data on the target machine — the installer can erase the entire disk. Install Ubuntu 26.04: Download the ISO…
The correct timezone affects more than the clock on your screen. It drives cron job scheduling, systemd timer execution, log file timestamps, database record timing, and SSL certificate validity checks. A mismatched timezone can cause scheduled jobs to fire at the wrong hour and make log timestamps impossible to match with real-world events. This guide shows how to change timezone on Ubuntu using the timedatectl command (the recommended approach for servers and remote machines) and through the graphical Date & Time settings on desktop systems. The steps apply to all current Ubuntu releases including 24.04 and 26.04. <strong>Prerequisite:</strong> Only the root user or a user with sudo access can change the system timezone. Check the Current Timezone Before You Change It Run timedatectl with no arguments to see the active timezone and clock status: bashtimedatectl Sample output: Local…
Atom is a free, open-source, cross-platform code editor developed by GitHub. Built on Electron, it uses…
MariaDB is an open-source, multi-threaded relational database management system and a fully backward-compatible replacement for MySQL.…