Before you touch Flannel, you need to master the basics of Kubernetes, the CNI concept, and Linux networking such as VXLAN, bridge, and routing. In this episode you set up a minimal two-node test cluster, install Flannel v0.28.x, and verify the installation for the first time.

Welcome to the Learn Flannel series! This series will take you through Flannel — the lightweight, simple CNI for connecting Pods across nodes in Kubernetes — from the fundamental concepts all the way to production readiness. There are 23 episodes in total, organized into six phases.
Before you touch flanneld, there are some basic skills and software you must have. Why are these prerequisites important? Because Flannel works at the host networking layer: it creates a VXLAN tunnel, manages routes in the kernel routing table, and coordinates with kubelet through CNI. If you don't understand the direction of data packets yet, all configuration will feel like guesswork.
Episode 0 is your roadmap: we will make sure you have the basic skills, set up a minimal two-node test cluster, install Flannel v0.28.x, and run the first verification. Once this episode is done, you can follow the rest of the series comfortably.
You should be comfortable with the concepts of Pod, Service, Deployment, and DaemonSet, because Flannel is installed as a DaemonSet on every node. Also understand the difference between the Pod network — per-Pod IP allocation — and the Service network — virtual IPs for load balancing. These two layers are handled by different components, and in episode 8 we will dissect the boundary between them.
kubectl cluster-info
kubectl get nodes -o wideMake sure kubectl get nodes runs without errors and shows all nodes in the Ready state.
CNI is the standard interface between the container runtime, such as containerd or CRI-O, and the networking plugin. When kubelet creates a new Pod, it calls the CNI plugin through the configuration in /etc/cni/net.d. Flannel provides the flannel plugin, which delegates bridge creation to the bridge plugin bundled with containernetworking/plugins. Understand the basic flow: a Pod is created, CNI is called, the veth interface is created, an IP is taken from the node's subnet, and then added to the bridge.
Flannel's default backend uses VXLAN, so understand UDP encapsulation. You also need to understand routes in the kernel, bridges, and the FDB. Make sure the br_netfilter, overlay, and vxlan kernel modules are available on all nodes:
lsmod | grep -E "br_netfilter|overlay|vxlan"
sudo modprobe br_netfilter
sudo sysctl -w net.bridge.bridge-nf-call-iptables=1The sudo modprobe br_netfilter command loads a module that has been important since kubeadm 1.30. We will go into the details more deeply in episode 7.
Install kubectl with a version that matches your cluster, plus helm as a second installation option:
kubectl version --client
helm versionYou will use kubectl apply for manifests and helm install for charts.
To test multi-node setups, use one of these approaches: kind, which runs all nodes in Docker containers; minikube with a specific driver; or kubeadm on virtual machines. You need at least two nodes: one control plane and one worker. Flannel's lightweight design fits kind particularly well because it boots fast and consumes few resources.
We will use Flannel v0.28.x, the latest stable version at the time of writing this series, through the official kube-flannel.yml manifest or the flannel/flannel Helm chart. CNI plugins such as bridge, portmap, and bandwidth are carried automatically by the Flannel DaemonSet and installed to /opt/cni/bin on each node.
Save the following configuration as kind-flannel.yaml:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
networking:
podSubnet: 10.244.0.0/16Then create the cluster:
kind create cluster --config kind-flannel.yaml
kubectl get nodes -o wideMake sure podSubnet: 10.244.0.0/16 is consistent with the Flannel default. If you use an existing cluster with a different CIDR, you must adjust net-conf.json as discussed in episode 3.
If you use kubeadm, initialize the control plane with the Flannel default CIDR:
sudo kubeadm init --pod-network-cidr=10.244.0.0/16After that, follow the kubeadm join command shown for each worker node.
On all nodes, make sure the bridge networking sysctl is active:
sudo modprobe br_netfilter
echo 1 | sudo tee /proc/sys/net/bridge/bridge-nf-call-iptablesThe net.bridge.bridge-nf-call-iptables=1 setting ensures packets from Pods that cross the bridge are also filtered by iptables. This is important for kube-proxy to work correctly and is mandatory since kubeadm 1.30.
For a single-node cluster, remove the control plane taint first, then install Flannel:
kubectl taint nodes --all node-role.kubernetes.io/control-plane-kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
kubectl get pods -n kube-flannel -o wideWait until all kube-flannel Pods reach the Running state, then check the overlay interface:
ip -d link show flannel.1Deploy two Pods spread across different nodes, then test the connection between them:
kubectl create deployment nginx --image=nginx --replicas=2
kubectl get pods -o wide
kubectl exec -it <pod-worker-1> -- curl <ip-pod-worker-2>If the curl succeeds, your environment is ready for the whole series. The details of what happens behind the scenes will be dissected starting in episode 2.
A recap of the prerequisites you prepared in episode 0:
In episode 0 you laid the foundation for the whole series: understanding the basic Kubernetes and Linux networking skills, building a multi-node test cluster, installing Flannel v0.28.x, and verifying Pod connectivity for the first time.
Key takeaways:
kube-flannel.yml manifest or the Helm chart.In the next episode, episode 1, we will discuss the history, background, and why you need Flannel — from Flannel's birth at CoreOS in 2014, the cross-node Pod connectivity problem it solved, to its position as one of the most widely adopted CNIs. Make sure your test cluster is ready, because the Learn Flannel journey is just beginning!