Step1: Disable swap memory: Kubernetes requires swap to be disabled for the kubelet to function correctly
# swapoff -a
# sudo sed -i '/ swap / s/^/#/' /etc/fstab
This disables swap immediately and make this change permanent
Step2: Load required kernel modules: Enable the overlay and br_netfilter modules needed for container networking
# cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
Run the following commands to load the modules
# modprobe overlay
# modprobe br_netfilter
These modules enable overlay networking and bridge netfilter functionality required by Kubernetes networking.
Step3: Configure sysctl parameters: Set kernel parameters for Kubernetes networking
# cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# sysctl --system
These settings enable iptables to process bridged traffic and allow IP forwarding between network interfaces.
Step4: Verify prerequisites: Confirm all settings are correctly applied
# free -h | grep -i swap && lsmod | grep -E "overlay|br_netfilter" && sysctl net.ipv4.ip_forward
Swap should show 0B, both kernel modules should be listed, and ip_forward should equal 1.
Step5: Install Container Runtime
Kubernetes requires a container runtime to run containers. Ubuntu 26.04 includes containerd in its default repositories, which is the recommended runtime for Kubernetes deployments.
Install containerd: Install the container runtime from Ubuntu repositories
# apt update && apt install containerd -y
Generate default configuration: Create the containerd configuration directory and generate a default config file
# mkdir -p /etc/containerd
# containerd config default | sudo tee /etc/containerd/config.toml
Step6: Enable SystemdCgroup: Configure containerd to use systemd as the cgroup driver, which is required for Kubernetes
# sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
This ensures containerd and kubelet use the same cgroup driver, preventing resource management conflicts.
Restart containerd: Apply the configuration changes
# systemctl restart containerd
# systemctl enable containerd
Step7: Install Kubernetes Components on Ubuntu 26.04
With the container runtime configured, you can now install the core Kubernetes components. The installation requires adding the official Kubernetes package repository to your system.
Install required packages: Install dependencies needed to add the Kubernetes repository
# apt install apt-transport-https ca-certificates curl gnupg conntrack -y
Add Kubernetes GPG key: Download and install the repository signing key
# curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key | sudo gpg --dearmor --yes -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
This key verifies the authenticity of packages downloaded from the Kubernetes repository.
Add Kubernetes repository: Add the official Kubernetes apt repository
# 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
Install Kubernetes packages: Update package lists and install kubeadm, kubelet, and kubectl
# apt update && apt install kubelet kubeadm kubectl -y
Hold packages: Prevent automatic upgrades that could break your cluster
# apt-mark hold kubelet kubeadm kubectl
Holding these packages ensures version consistency across your cluster and prevents unintended upgrades during system updates.
Step8: Initialize the Kubernetes Cluster
Now you can initialize your Kubernetes cluster using kubeadm. This process creates the control plane components and configures the cluster for operation.
Initialize the cluster: Run kubeadm init with the pod network CIDR
# kubeadm init --pod-network-cidr=10.244.0.0/16
The --pod-network-cidr flag specifies the IP address range for pod networking. The value 10.244.0.0/16 is compatible with Flannel, which we will install in the next section.
Sample output after the "kubeadm init"
-----------------
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.192.134:6443 --token gh7gno.yworbsfmf69g12g2 \
--discovery-token-ca-cert-hash sha256:1bd571d1a1993841478caf85a7f150100daa5062ddb00f05b8f4c8d3f67c7ace
-----------------
Configure kubectl for your user: Set up kubectl access for the regular user. Run the following command as the regular user
$ mkdir -p $HOME/.kube
$ cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
These commands copy the cluster admin configuration to your home directory and set proper ownership, allowing you to run kubectl commands without sudo.
Remove control plane taint (single-node only): Allow scheduling pods on the control plane node
$ kubectl taint nodes --all node-role.kubernetes.io/control-plane-
By default, Kubernetes prevents workloads from running on control plane nodes. For a single-node development cluster, removing this taint allows pods to be scheduled on the only available node.
Step9: Install Network Plugin
A pod network plugin is required for pods to communicate with each other across the cluster. Flannel is a simple and reliable choice that works well for most deployments.
$ kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
This command downloads and applies the Flannel configuration, creating the necessary DaemonSet and ConfigMap resources
Verify network plugin deployment: Check that Flannel pods are running
$ kubectl get pods -n kube-flannel
Wait until all Flannel pods show a Running status before proceeding.
Verify Kubernetes Installation on Ubuntu 26.04
Check node status: Verify the node is ready
$ kubectl get nodes
Output should show your node with STATUS “Ready”. If the status shows “NotReady”, wait a few moments for the network plugin to initialize.
Check system pods: Verify all Kubernetes system components are running
$ kubectl get pods -n kube-system
All pods should show Running or Completed status.
Step10: Deploy a test application: Create a test pod to verify the cluster accepts workloads
$ kubectl run nginx --image=nginx --port=80
$ kubectl get pods
The nginx pod should transition to Running status within a minute, confirming that your cluster can pull images and schedule pods.
Accessing the nginx webserver outside the cluster for a local development setup
If you are running on-premises or using a local setup like Minikube/Kind where cloud load balancers aren't available, NodePort service is your go-to.
How it works:
Kubernetes opens a specific port (by default, in the high range of 30000–32767) on every single node (machine) in your cluster. Any traffic hitting that port on any node's IP address will be forwarded to your Nginx web server.
Label nginx pod so that it can be selected in service manifest for the pod discovery
$ kubectl label pod nginx app=nginx-webserver
vi nginx-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx-webserver
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 32080 # Optional: Specify a port in the 30000-32767 range
$ kubectl apply -f nginx-nodeport.yaml
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-nodeport NodePort 10.101.244.247 <none> 80:32080/TCP 18m
Find the nodes IP address and access over the browser as http://NODE_IP_ADDRESS:32080
Sample output:
