propeller logo

K8s single node setup for CoCo

How to setup a single node k8s cluster using kubeadm and containerd on a Linux machine for testing and developing with confidentail containers.

This tutorial sets up a single machine as both control-plane + worker, using kubeadm (the standard Kubernetes bootstrapping tool) and containerd (recommended runtime). It’s the closest thing to a “real” DIY cluster you’ll run on one box.

Assumptions:

  • Ubuntu/Debian-like Linux (examples use apt)
  • You have sudo access
  • You want a single-node cluster that can run workloads (not just control plane)

0. Prep the machine

sudo hostnamectl set-hostname k8s-single

Disable swap (required by kubelet)

sudo swapoff -a
sudo sed -i.bak '/\sswap\s/s/^/#/' /etc/fstab

Load kernel modules + sysctl for Kubernetes networking

cat <<'EOF' | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

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

sudo sysctl --system

1. Install containerd (container runtime)

Kubernetes needs a container runtime; containerd is a common choice.

Install + enable

sudo apt-get update
sudo apt-get install -y containerd
sudo systemctl enable --now containerd

Kubeadm clusters work best when the runtime cgroup driver matches kubelet (often systemd).

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null

# Set SystemdCgroup = true
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

sudo systemctl restart containerd

2. Install kubeadm / kubelet / kubectl

Follow the official “Installing kubeadm” instructions for your distro/version.

On Ubuntu/Debian this typically means:

  • add the Kubernetes package repo for your target version

  • install:

    • kubelet (node agent)

    • kubeadm (cluster bootstrap)

    • kubectl (CLI)

After installing, verify:

kubeadm version
kubectl version --client

The output should be similar to:

$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"34", EmulationMajor:"", EmulationMinor:"", MinCompatibilityMajor:"", MinCompatibilityMinor:"", GitVersion:"v1.34.1", GitCommit:"93248f9ae092f571eb870b7664c534bfc7d00f03", GitTreeState:"clean", BuildDate:"2025-09-09T19:43:15Z", GoVersion:"go1.24.6", Compiler:"gc", Platform:"linux/amd64"}
$ kubectl version --client
Client Version: v1.34.1
Kustomize Version: v5.7.1

3. Bootstrap the single-node cluster (control plane)

Pick a Pod network CIDR that matches your CNI choice.

  • For Calico, a common choice is 192.168.0.0/16.

Initialize

sudo kubeadm init --pod-network-cidr=192.168.0.0/16

kubeadm init creates the control plane node.

Configure kubectl for your user

Run the exact commands printed at the end of kubeadm init, typically:

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

Check node status:

kubectl get nodes

4. Install a CNI (pod network) — example: Calico

Kubernetes won’t schedule pods normally until a CNI is installed.

Install Calico (manifest method)

curl -LO https://raw.githubusercontent.com/projectcalico/calico/v3.31.3/manifests/calico.yaml
kubectl apply -f calico.yaml

Calico’s docs note that if you use the default 192.168.0.0/16, you usually don’t need to edit the manifest.

Wait for pods:

kubectl get pods -A --watch

5. Make it a true single-node cluster (schedule workloads on control plane)

By default, control-plane nodes repel normal workloads via a taint. You remove it for single-node setups.

kubectl taint nodes --all node-role.kubernetes.io/control-plane-

Confirm:

kubectl get nodes -o wide

If everything was completed, run a quick test.

On this page