Search for a command to run...
Install Kubernetes v1.37 with kubeadm, kubelet and kubectl from the official pkgs.k8s.io repositories, configure containerd, install a CNI and join worker nodes.
This guide installs Kubernetes v1.37 with kubeadm, kubelet and kubectl using the
official community package repositories, containerd as the container runtime and Calico as
the pod network.
The old apt.kubernetes.io and yum.kubernetes.io repositories were frozen in September
2023 and no longer receive packages. Everything below uses pkgs.k8s.io, which serves
builds from v1.24 onwards. There is a dedicated repository per minor version — for
example v1.37.
containerd installed and configured to use the systemd cgroup driverKubernetes talks to the runtime through the CRI. containerd is the recommended default.
# Debian / Ubuntu
sudo apt update
sudo apt install -y containerd
# RHEL / CentOS / Fedora
sudo dnf install -y containerdEnable the br_netfilter module and the systemd cgroup driver:
# Load the modules required by Kubernetes
sudo modprobe overlay
sudo modprobe br_netfilter
# Make them persistent across reboots
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
# Configure the bridge to forward iptables traffic
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
# Generate the default containerd configuration and switch it to systemd cgroups
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl enable --now containerd
sudo systemctl restart containerd
# Install crictl for debugging the runtime (shipped separately from containerd)
CRICTL_VERSION=v1.37.0
curl -fsSL -o /tmp/crictl.tar.gz \
"https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-amd64.tar.gz"
sudo tar -C /usr/local/bin -xzf /tmp/crictl.tar.gz
# Confirm the runtime is healthy
sudo crictl info | head -n 5Swap is off by default in kubeadm pre-flight checks:
# Disable swap for the running system
sudo swapoff -a
# Comment out swap entries in /etc/fstab so they do not come back after a reboot
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstabSince Kubernetes 1.28 the kubelet can be configured to use swap via the
memorySwap.swapBehavior setting in the kubelet configuration file. Swap is still
discouraged for performance reasons, and the default behaviour is to fail the pod.
The steps below are identical for the control plane and every worker node.
# Create the keyring directory (not present by default before Debian 12 / Ubuntu 22.04)
sudo mkdir -p -m 755 /etc/apt/keyrings
# Download the signing key for the v1.37 package repositories
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.37/deb/Release.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# Add the v1.37 repository
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.37/deb/ /' \
| sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
# Install the components
sudo apt install -y kubelet kubeadm kubectl
# Hold the versions so an upgrade never happens automatically
sudo apt-mark hold kubelet kubeadm kubectl
# Enable the kubelet (it will crash-loop until kubeadm init runs)
sudo systemctl enable --now kubeletBefore you start, decide on two values:
10.244.0.0/16 is a safe default.10.96.0.0/12 is the
Kubernetes default.Create a kubeadm configuration file so the cluster is reproducible:
# Create the working directory
mkdir -p ~/kubeadm-config && cd ~/kubeadm-config
# Write the configuration
cat <<EOF > kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.1.10
bindPort: 6443
nodeRegistration:
criSocket: unix:///run/containerd/containerd.sock
kubeletExtraArgs:
- name: cgroup-driver
value: systemd
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.37.1
networking:
podSubnet: 10.244.0.0/16
serviceSubnet: 10.96.0.0/12
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOF
# Initialise the control plane
sudo kubeadm init --config kubeadm-config.yaml --upload-certsPassing --upload-certs lets additional control-plane nodes join later with
kubeadm join --control-plane. If you plan to convert this cluster to a highly
available topology, create a load balancer and set --control-plane-endpoint to its
DNS name from the start — it cannot be added later.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# Make it the default for future shells
echo 'export KUBECONFIG=$HOME/.kube/config' >> $HOME/.bashrc
source $HOME/.bashrcCoreDNS stays in Pending until a CNI plugin is installed. Calico is the most common
choice:
# Install Calico
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
# Wait for the network to become ready
kubectl wait --for=condition=Ready nodes --all --timeout=300sFor a managed installation, the Tigera operator offers pod-to-pod encryption, network policy and BGP routing. See the Calico documentation.
kubeadm init prints the exact command to run on each worker. Use the token it gives you:
sudo kubeadm join 192.168.1.10:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>The token is a credential — anyone holding it can add a node to your cluster. Rotate it regularly:
# List the active tokens
sudo kubeadm token list
# Create a new token
sudo kubeadm token create
# Delete all expired tokens
sudo kubeadm token delete <token-id># All nodes should report Ready
kubectl get nodes -o wide
# All system pods should be Running
kubectl get pods -n kube-system
# Check the control plane health
sudo kubeadm status
# Confirm the container runtime is wired up correctly
sudo crictl ps -a
# Deploy a test workload
kubectl create deployment hello --image=nginx:alpine
kubectl expose deployment hello --type=NodePort --port=80
kubectl get svc helloUpgrades must go one minor version at a time (for example 1.35 to 1.36 to 1.37).
# On the control plane: upgrade the packages
sudo apt update && sudo apt install -y kubeadm=1.37.1-1.1
sudo apt-mark unhold kubeadm && sudo apt-mark hold kubeadm
# Check what the upgrade will do
sudo kubeadm upgrade plan
# Apply the control plane upgrade
sudo kubeadm upgrade apply v1.37.1
# On every worker node: upgrade the packages
sudo apt update && sudo apt install -y kubelet=1.37.1-1.1 kubectl=1.37.1-1.1
sudo apt-mark unhold kubelet kubectl && sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# Back on the control plane, approve the node upgrade
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
sudo kubeadm upgrade node
kubectl uncordon <node-name>Enforce the restricted Pod Security Standard
kubectl label --overwrite ns default pod-security.kubernetes.io/enforce=restrictedDeny traffic by default with NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- EgressUse RBAC instead of cluster-admin
kubectl create role pod-reader --verb=get,list --resource=pods
kubectl create rolebinding pod-reader-binding --role=pod-reader --user=developerEncrypt secrets at rest
sudo kubeadm init phase feature enable encryption --config=encryption-config.yamlBack up etcd regularly
sudo ETCDCTL_API=3 etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /var/backups/etcd-$(date +%F).dbInstall Docker Engine 29 on Linux, macOS and Windows using the official apt, dnf and package-manager repositories, then configure the daemon, Buildx and Compose.
Install Helm 4 on Linux, macOS and Windows, verify the binary signature, configure repositories and learn the new Helm 4 features such as OCI digest installs and server-side apply.