KubernetesXXXVI · CNIContainer Network Interface
CNI specification — the contract between kubelet and the network plugin
What you'll learn
- State the responsibilities of the CNI plugin under the specification
- Read a CNI configuration file and predict runtime behaviour
- Explain why the CNI is a binary contract rather than a Kubernetes API
- Identify the operational failure modes of CNI plugin choice
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
The Container Network Interface (CNI) is a specification, not a Kubernetes concept. It defines a binary contract between a runtime (the kubelet, containerd, CRI-O, Podman) and a plugin (Calico, Cilium, Flannel, Weave, Multus). The runtime invokes the plugin with JSON over stdin; the plugin mutates the network namespace and returns a JSON result on stdout. Kubernetes inherits the contract because the kubelet is the runtime. This lesson walks the contract, the configuration, and the operational discipline of treating the CNI as a binary contract.
Why a specification rather than an API
The CNI is a specification because the number of network
implementations is large and the number of ways to call
them is small. If every CRI implemented networking
itself, every CNI author would write a custom adapter
for every runtime. The CNI inverts the dependency: the
runtime writes one binary invocation, and every plugin
implements that one invocation. The result is that
containernetworking/cni ships a single libcni that
the kubelet calls, and the same plugin binary works
under any runtime that follows the spec.
sequenceDiagram
autonumber
participant K as kubelet
participant CNI as libcni
participant P as CNI plugin
participant NS as Pod netns
K->>CNI: ADD (ContainerID, NetNS, IfName, args)
CNI->>P: exec plugin with JSON
P->>NS: create veth, assign IP, configure routes
P-->>CNI: result JSON (IPs, interfaces)
CNI-->>K: cached result
The kubelet never speaks IPAM, BGP, eBPF, or VXLAN. It speaks CNI. The plugin is free to do anything behind that interface, and the runtime is free to call any plugin that respects the interface.
The three operations
The CNI specification defines three operations:
| Operation | Trigger | Plugin must |
|---|---|---|
ADD | Container created | Connect the container to the network and report the result |
DEL | Container deleted | Remove the container from the network |
CHECK | Container status query | Report whether the network configuration is still correct |
Every operation is a synchronous, blocking call. The runtime passes the following to the plugin’s stdin:
{
"cniVersion": "1.0.0",
"name": "k8s-pod-network",
"type": "calico",
"container": "abc123",
"ifname": "eth0",
"netns": "/var/run/netns/abc123",
"args": {
"KUBERNETES_POD_NAMESPACE": "prod-app",
"KUBERNETES_POD_NAME": "billing-7d4",
"KUBERNETES_POD_INFRA_CONTAINER_ID": "abc123"
},
"ipam": {
"type": "calico-ipam"
}
}
The plugin returns:
{
"cniVersion": "1.0.0",
"interfaces": [
{ "name": "eth0", "mac": "aa:bb:cc:dd:ee:ff" }
],
"ips": [
{ "version": "4", "address": "10.244.1.5/32", "interface": 0 }
],
"routes": [
{ "dst": "0.0.0.0/0", "gw": "169.254.1.1" }
],
"dns": {
"nameservers": ["10.96.0.10"],
"domain": "cluster.local",
"search": ["prod-app.svc.cluster.local", "svc.cluster.local", "cluster.local"]
}
}
The runtime parses the result, caches it, and (in Kubernetes) writes the IP into the Pod’s status.
The configuration file
The configuration file lives on the node at
/etc/cni/net.d/. The kubelet reads the first file
whose name matches [0-9]*-*.conflist or *.conf and
passes its contents to libcni on every ADD. Here is
a Calico conflist:
{
"name": "k8s-pod-network",
"cniVersion": "1.0.0",
"plugins": [
{
"type": "calico",
"log_level": "info",
"datastore_type": "kubernetes",
"policy": {
"type": "k8s"
},
"kubernetes": {
"kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
The plugins array is a chain. libcni calls each
plugin in order on ADD, and reverses the order on DEL.
This is how Calico delegates portmap to a built-in
plugin without re-implementing host-port support.
Where the configuration lives
The kubelet reads:
ls /etc/cni/net.d/
10-calico.conflist
calico-kubeconfig
The file with the lowest leading number wins when both
a .conflist and a .conf exist. The convention
10-calico.conflist exists so operators can place
multiple plugins in priority order without collisions.
The CNI_VERSION field
cniVersion is the version of the spec the
configuration targets, not the version of the plugin.
A plugin that supports 1.0.0 can be invoked with a
config that declares 0.4.0; libcni adapts the JSON
fields. The current stable spec is 1.0.0. Kubeadm
clusters shipped with Kubernetes 1.34 default to
1.0.0 for CNI 1.x plugins.
The CNI’s relationship to Kubernetes
Kubernetes defines the abstract networking model
(the four rules). The CNI is the implementation of
that model. The kubelet has no opinion on routing, IPAM,
or policy; it forwards the CNI’s result to the API
server as the Pod’s podIP. The CNI is the source of
truth for what the Pod’s IP actually is.
status:
podIP: 10.244.1.5
podIPs:
- ip: 10.244.1.5
The podIP is whatever the CNI returned. A bug in the
CNI produces a bug in the Pod’s IP. There is no
validation between the CNI’s output and what the API
server stores.
The CNI’s failure modes
The CNI’s failure modes are the basis of the operational discipline:
- Plugin not installed: the kubelet retries ADD
until the plugin binary appears. Pods stay in
ContainerCreating. - Plugin binary crashes: the kubelet logs the non-zero exit; the Pod stays pending.
- Conflicting conflists: the kubelet picks the first alphabetically; the wrong plugin may be loaded.
- Permission denied: the plugin cannot read
/var/lib/cni/resultsor the kubeconfig; the kubelet logs the failure. - IPAM exhaustion: the plugin returns an error; Pods stay pending until an IP is freed.
Every failure mode is per-node. A bug in the CNI on node-1 affects Pods scheduled to node-1 only.
The CNI’s operational discipline
The CNI’s operational discipline:
- Version the plugin binary. The binary is the cluster’s networking implementation; pin it.
- Version the conflist. The conflist is part of the cluster’s GitOps; never edit it live.
- Audit the conflist at every release. A typo in
cniVersionblocks every Pod on the node. - Test the CNI in staging. The CNI is critical infrastructure; promote through staging before production.
- Monitor the kubelet’s CNI logs. The logs are the single source of truth for CNI failures.
- Disable the wrong plugin. The conflist lives on disk; a stale file with a lower number wins.
Quiz
Knowledge check · 4 questions
Q1. What does the CNI specification define?
Q2. The CNI plugin communicates with the runtime over the Kubernetes API rather than over stdin/stdout.
Q3. A new node is added to the cluster. Pods scheduled to it stay in ContainerCreating with the kubelet reporting 'cni plugin not initialized'. What is the diagnostic flow?
Node-5 was joined with kubeadm. The kubelet is running; the container runtime is healthy. The conflist exists at /etc/cni/net.d/10-calico.conflist according to the operator. Pods stay in ContainerCreating; the kubelet log shows 'cni plugin not initialized'.
Q4. Name two operational practices that prevent CNI plugin failures from reaching production.
Passing score: 75%. Answers are checked in this browser.
Production discipline
- The CNI is a binary contract. The kubelet invokes the plugin; the plugin configures the network namespace. The contract is enforced by the OS process boundary.
- The CNI is per-node. A bug in the plugin on node-1 affects Pods on node-1 only. The blast radius is the node, not the cluster.
- Version the plugin binary and the conflist. Both
live in
/etc/cni/net.d/and/opt/cni/bin/. Both are part of the cluster’s GitOps. - Audit the conflist at every release. A typo blocks every Pod scheduled to the node.
- Test the CNI in staging. The CNI is critical infrastructure; promote through staging before production.
- Monitor the kubelet’s CNI logs. The logs are the single source of truth for CNI failures.
- Document the CNI choice. The CNI is the cluster’s networking implementation; the documentation is the cluster’s networking reference.