Remote & on-site IT support across Australia & New Zealand · 24/7 emergency line

Install and Configure Kubernetes on Ubuntu 25.10

Prepare Ubuntu for installation
# Update the system packages
sudo apt update && sudo apt upgrade -y

# Disable Swap to ensure predictable memory management on Master and Nodes
sudo swapoff -a
sudo sed -i '/swap/s/^/#/' /etc/fstab

# Load Required Kernel Modules on boot
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter

# Ensure bridged IPv4/IPv6 traffic is visible to iptables
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sudo sysctl --system

# Validate
lsmod | grep -E 'br_netfilter|overlay'
sysctl net.ipv4.ip_forward
sysctl net.bridge.bridge-nf-call-iptables
swapon --show     # (should print nothing)
Install Docker on both Master and Nodes
# Add Docker's official GPG key:
sudo apt install ca-certificates curl gnupg lsb-release -y
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

# Generate default config for containerd
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml

# Set cgroup driver to systemd
sudo mkdir -p /etc/docker
cat <<'EOF' | sudo tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": { "max-size": "100m" },
  "storage-driver": "overlay2"
}
EOF

# Enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable --now docker
sudo systemctl restart docker
sudo systemctl restart containerd
sudo systemctl enable containerd

# Add user for docker
sudo adduser --system --group --home /opt/$USER $USER
# Add user to docker group
sudo usermod -aG docker $USER
# Refresh group membership without logging out
newgrp docker
docker ps

# Validate
docker info --format '{{.CgroupDriver}}' # should be: systemd
systemctl is-active docker # active
Install cri-dockerd (all nodes)

The default CRI socket is /run/cri-dockerd.sock. [Kubernetes Docker Engine]

wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.4.2/cri-dockerd_0.4.2.3-0.debian-bookworm_amd64.deb

sudo dpkg -i cri-dockerd_0.4.2.3-0.debian-bookworm_amd64.deb

sudo apt install -f -y

# enable socket/service
sudo systemctl enable --now cri-docker.socket
sudo systemctl enable --now cri-docker.service

# Validate
systemctl is-active cri-docker.socket
systemctl is-active cri-docker.service
ss -ltn | grep -E '(/run/cri-dockerd.sock)?' # optional
which cri-dockerd
Install kubeadm, kubelet, and kubectl
# Add Kubernetes APT repository
sudo apt update -y
sudo apt install -y apt-transport-https ca-certificates curl gpg

# Create keyrings directory
sudo mkdir -p /etc/apt/keyrings

# Download and store the GPG key
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key 
  | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# Add the repository
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] 
https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /" 
| sudo tee /etc/apt/sources.list.d/kubernetes.list

# Update package list
sudo apt update

# Install Kubernetes tools on both Master and Nodes
sudo apt install -y kubelet kubeadm kubectl

# Prevent automatic updates
sudo apt-mark hold kubelet kubeadm kubectl

# Validate
kubeadm version && kubelet --version && kubectl version --client

systemctl enable - now kubelet || true # kubelet may stay in a waiting state until init/join
Initializing the Control Plane (on Master Node Only)
sudo kubeadm init --apiserver-advertise-address=10.254.x.x --pod-network-cidr=192.168.0.0/16 --cri-socket=unix:///run/cri-dockerd.sock

# To start using your cluster
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

# Validate
kubectl cluster-info
kubectl get nodes -o wide # control-plane will be NotReady until CNI is installed

# Install a Pod Network (CNI Plugin)
# Install operator + CRDs (use latest v3.xx from docs page; example uses v3.31.4)
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.4/manifests/operator-crds.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.31.4/manifests/tigera-operator.yaml
# Get default custom-resources (edit if you want to change IP pool/CIDR/encapsulation)
curl -O -L https://raw.githubusercontent.com/projectcalico/calico/v3.31.4/manifests/custom-resources.yaml
kubectl create -f custom-resources.yaml

# Validate
kubectl -n tigera-operator rollout status deploy/tigera-operator
watch kubectl get pods -n calico-system
kubectl get nodes # should turn Ready once calico-node is up

# Wait for the pods in kube-system to be ready:
kubectl get pods -A
Join Worker Nodes
# on master-1, print a join command (no TTL so you can reuse it today)
kubeadm token create --print-join-command --ttl 0

# On each work node
sudo kubeadm join <master_ip>:<master_port> --token <token> --discovery-token-ca-cert-hash sha256:<hash> --cri-socket=unix:///run/cri-dockerd.sock

# Validate on Master
kubectl get nodes -o wide
kubectl get pods -A
(Optional) UFW / security-group ports to allow

Control-plane node needs inbound:

6443/tcp (API)

10250/tcp (kubelet)

10257/tcp (kube-controller-manager)

10259/tcp (kube-scheduler).

etcd (2379–2380) is local unless externalized.

NodePort range 30000–32767/tcp;

kubelet 10250/tcp.

Install Dashboard
1. Install Helm
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

2. Add the updated repository
helm repo add kubernetes-dashboard https://kubernetes-retired.github.io/dashboard/

3. Update your local chart cache
helm repo update

4. Install
helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard 
--create-namespace --namespace kubernetes-dashboard

5. Create dashboard-admin.yaml
apiVersion: v1
kind: Secret
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
  annotations:
    kubernetes.io/service-account.name: "admin-user"   
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard

6. Apply the Configuration
kubectl apply -f dashboard-admin.yaml

7. Generate the Login Token
kubectl get secret admin-user -n kubernetes-dashboard -o jsonpath="{.data.token}" | base64 -d

8. Install socat on Master and Nodes
sudo apt update && sudo apt install -y socat

9. Start Port forwarding
kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard-kong-proxy 8443:443 --address 0.0.0.0

10. Login
Setup TLS Secret
# Create the tls secret from your ssl certificate
sudo kubectl create secret tls my-tls-secret 
  --cert=/etc/ssl/private/example.com.crt 
  --key=/etc/ssl/private/example.com.key 
  --kubeconfig=$HOME/.kube/config -n namespace (default)

secret/my-tls-secret created

# Get the secret
kubectl get secret my-tls-secret

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top