What Is CNI in Docker? A Complete Guide to Container Network Interface Plugins
From a single bridge network to pluggable, production-grade networking — how CNI plugins work, why Docker's own model is different, and how the two worlds meet in real infrastructure.
Imagine you just moved into a new apartment building. Before you can get water, electricity, or internet, someone has to physically connect your unit to the building’s utility lines. Containers face the exact same problem the instant they are born: a freshly created container is like an apartment with no pipes and no wires. It cannot talk to other containers, it cannot reach the internet, and nothing outside can reach it either. Something has to lay down that “plumbing” — assign it an IP address, wire it into a virtual network, and set up the routing rules that let traffic flow in and out. That “something,” in a huge slice of the modern container world, is a small, specialized program called a CNI plugin. This guide walks through exactly what CNI is, how it works step by step, where it fits (and where it does not fit) in Docker specifically, and how to use it safely and correctly in real production systems.
1Core Concepts: What Container Networking Actually Solves
A container, at its heart, is just an isolated process running on a host machine. Linux gives it isolation through two building blocks: namespaces (which give the process its own private view of things like network interfaces, process IDs, and mount points) and cgroups (which limit how much CPU, memory, and I/O it can consume). The network namespace is the one that matters here — when a container is created, Linux can give it a completely empty, private network stack: no interfaces, no routes, no IP address, nothing. It is a sealed room with no doors.
Someone has to open a door. That means: creating a virtual network interface inside the container’s namespace, connecting the other end of that interface to something on the host (usually a virtual bridge or switch), assigning an IP address to the container’s side, and adding routing rules so packets know where to go. This whole sequence is called network plumbing, and every container platform — Docker, Kubernetes, containerd, Podman — needs a consistent, repeatable way to do it every single time a container starts.
Think of a shipping port. Every ship (container) that docks needs a crane operator to connect it to the port’s water, fuel, and cargo lines using the exact same standardized couplings, no matter which shipping company built the ship. CNI is that standardized coupling — a fixed “socket” that lets any container runtime plug into any networking solution, as long as both sides follow the same interface.
Two competing ideas emerged to standardize this plumbing: Docker built its own model called the Container Network Model (CNM), implemented through a library called libnetwork. Separately, CoreOS (the company behind rkt, an early Docker competitor) proposed a simpler, more minimal specification called the Container Network Interface (CNI). CNI was later donated to the Cloud Native Computing Foundation (CNCF) and became the networking standard used by Kubernetes and most of the container ecosystem outside Docker itself. Understanding both models — and how they differ — is the key to understanding this entire topic, because it explains why the question “what is CNI in Docker?” has a more nuanced answer than a simple one-line definition.
2What CNI Actually Is
CNI stands for Container Network Interface. Despite the name, it is not a piece of software you install and run — it is a specification, a contract. It defines, in very precise terms, how a container runtime (the software that creates and manages containers) should talk to a network plugin (the software that actually sets up networking) so the two can work together no matter who wrote either side.
The contract is deliberately tiny. A CNI plugin is just an executable binary that understands four operations, delivered as commands with a JSON configuration piped into its standard input:
ADD
Attach a container’s network namespace to the network — create the interface, assign an IP, and set up routes.
DEL
Tear everything down when the container is removed — release the IP, delete the interface, clean up routes.
CHECK
Verify that a previously configured network is still correctly set up and healthy.
VERSION
Report which versions of the CNI spec this particular plugin supports.
That’s it. Four verbs. Everything else — how the IP address is chosen, whether encryption is used, whether there is a firewall in front of the container — is left entirely up to whichever plugin implements those four verbs. This minimalism is exactly why CNI spread so widely: writing a new plugin is a small, well-defined task, so an entire ecosystem of networking vendors (Calico, Cilium, Flannel, Weave, AWS VPC CNI, Azure CNI, and dozens more) could all build compatible plugins independently, and any CNI-compatible runtime could use any of them interchangeably.
CNI is a specification, not a product. “Installing CNI” really means installing a specific CNI-compliant plugin (like Calico) plus the small set of binaries and config files that let a runtime discover and invoke it.
3Docker’s Own Model: CNM, Not CNI
Classic Docker Engine — the docker run, docker network create world most people picture when they hear “Docker” — does not use CNI by default. Docker built and uses its own model, the Container Network Model (CNM), implemented by an internal library called libnetwork. When you run docker network create mynet or attach a container to the default bridge network, you are watching CNM in action, not CNI.
CNM organizes networking around three concepts instead of CNI’s four commands:
| CNM Concept | What It Means |
|---|---|
| Network Sandbox | The isolated network stack for one container (its namespace, interfaces, routes, DNS config) |
| Endpoint | A virtual network interface that joins a sandbox to a network — like a network card |
| Network | A group of endpoints that can freely communicate — e.g. a bridge network or overlay network |
Docker ships built-in CNM drivers for common cases: bridge (the default, a private virtual switch on a single host), host (the container shares the host’s network stack directly, no isolation), overlay (a virtual network spanning multiple hosts, used by Docker Swarm), macvlan (gives a container its own MAC address on the physical network, as if it were a real device), and none (no networking at all). These drivers are conceptually similar in purpose to CNI plugins — they both “plumb” a container into a network — but they speak a completely different internal protocol and cannot be swapped for a CNI plugin without extra glue.
CNM (Docker Native)
- Built into Docker Engine, zero extra install
- Simple for single-host use cases
- Tightly integrated with Docker Swarm
CNI (External Standard)
- Not natively spoken by Docker Engine
- Requires a runtime that understands CNI (Kubernetes, containerd directly)
- Huge, vendor-neutral plugin ecosystem
So where does the question “CNI in Docker” come from, if Docker Engine does not speak CNI? Three real situations explain it, and they are the reason this topic causes so much confusion online.
4The Three Places CNI Actually Meets Docker
1. Kubernetes Running on Top of Docker (Historical)
For years, the most common way to run Kubernetes was with Docker Engine as the container runtime underneath it, via a compatibility shim called dockershim. In this setup, Kubernetes itself — not Docker — called the CNI plugin to wire up pod networking, while Docker was only responsible for pulling images and running the container processes. People described this whole stack loosely as “CNI in Docker,” even though Docker’s own daemon never invoked CNI directly; Kubernetes did. Kubernetes removed dockershim in version 1.24 (2022), so this exact arrangement is now legacy, though it explains most of the historical documentation and StackOverflow answers still floating around.
2. containerd — The Engine Underneath Docker — Speaks CNI Natively
Modern Docker Engine is actually built on top of a lower-level runtime called containerd (Docker donated it to the CNCF in 2017). Containerd has first-class, built-in CNI support and uses it directly when it is driven by Kubernetes (via the CRI, Container Runtime Interface) or by other CNI-aware tooling — completely bypassing Docker’s own CNM layer. So the containerd process sitting underneath your Docker installation is fully CNI-capable, even though the Docker CLI and daemon layered on top of it are not the ones invoking those plugins in a typical docker run.
3. Docker Swarm and Third-Party CNI Bridges
A small number of third-party and experimental projects exist to bridge CNI plugins into Docker Swarm or plain Docker networking, letting a CNI plugin like Calico or Weave manage IP address allocation and policy for Docker containers via a custom network driver plugin (Docker does support pluggable network drivers through its own plugin API — a different mechanism from CNI, but often confused with it because the vendor is the same, e.g. “Weave Net” ships both a Docker network plugin and a CNI plugin from one codebase).
“Docker uses CNI” is only true indirectly, through containerd or through Kubernetes sitting on top of Docker-family runtimes. A plain docker run on a developer laptop is using CNM/libnetwork, full stop.
5Architecture and Components of a CNI Setup
A working CNI installation is made of a handful of concrete pieces sitting on disk, not one monolithic program. Understanding each piece makes debugging real clusters dramatically easier.
CNI Plugin Executable
A single compiled binary (e.g. calico, bridge, flannel) placed in a well-known directory, typically /opt/cni/bin.
Network Configuration File
A JSON file in /etc/cni/net.d/ describing which plugin(s) to run and with what parameters for a given network.
Container Runtime / Orchestrator
The caller — containerd, CRI-O, or Kubernetes’ kubelet — that invokes the plugin at the right lifecycle moment.
IPAM Data Store
Wherever IP address allocation state is tracked — local files, etcd, or a cloud provider’s IP address management service.
Plugins are frequently chained. A single network configuration file can list several plugins that run in sequence, each one enriching the result of the previous one. A very common chain looks like: a main plugin (e.g. bridge or calico) creates the interface and wires it to the network, an IPAM plugin (e.g. host-local or whereabouts) hands out the IP address, and one or more meta plugins (e.g. portmap, bandwidth, tuning) apply extra behavior like port forwarding or traffic shaping on top.
graph TB
RT["Container Runtime
(containerd / kubelet)"] -->|invokes with JSON config| MAIN["Main Plugin
e.g. Calico / Bridge"]
MAIN --> IPAM["IPAM Plugin
e.g. host-local"]
IPAM -->|assigns IP| MAIN
MAIN --> META1["Meta Plugin
portmap"]
META1 --> META2["Meta Plugin
bandwidth"]
META2 --> NS["Container Network Namespace"]
MAIN -.->|creates veth pair| BR["Host Bridge / Overlay"]
BR --> HOSTNET["Host Network Stack"]
“Why does CNI separate the main plugin from the IPAM plugin instead of bundling address allocation into every plugin?” — Strong answer: separation of concerns. IP address management is a genuinely independent problem (avoiding collisions, respecting subnets, integrating with cloud IPAM services), so letting any main plugin delegate to any IPAM plugin avoids every network plugin author reinventing address allocation, and lets operators swap allocation strategies without touching the networking logic itself.
6Internal Working: What Happens Inside a Plugin Call
When the runtime decides a container needs networking, it does not open a persistent connection to a daemon — it simply executes the plugin binary as a short-lived process, exactly once per operation, and communicates with it through three channels: environment variables, standard input, and the exit code.
Runtime creates the network namespace
Before calling any plugin, the runtime creates an empty Linux network namespace for the new container — the “sealed room” from Chapter 1.
Runtime sets environment variables
Variables like CNI_COMMAND=ADD, CNI_CONTAINERID, CNI_NETNS (path to the namespace), and CNI_IFNAME (desired interface name, usually eth0) are exported for the plugin to read.
Runtime pipes the JSON config to stdin
The network configuration file’s JSON content (plugin type, subnet, MTU, any custom fields) is written to the plugin process’s standard input.
Plugin performs the actual networking work
The plugin enters the given namespace, creates a virtual ethernet (veth) pair — one end stays on the host, the other is moved inside the container namespace — and wires the host end to a bridge, an overlay tunnel, or a physical interface.
Plugin delegates to IPAM if chained
If an IPAM plugin is configured, the main plugin invokes it the same way it was invoked — same environment-variable-and-stdin pattern — and receives back an assigned IP address and gateway.
Plugin returns structured JSON on stdout
The plugin prints a JSON result describing the interfaces created and IPs assigned, which the runtime parses and stores for later use (e.g. to answer kubectl get pod -o wide).
Because every step is stateless and process-based, CNI plugins are unusually easy to test in isolation — you can literally invoke one by hand from a shell with the right environment variables set, which is exactly how experienced engineers debug broken pod networking in production.
7Data Flow and Lifecycle of a Networked Container
Zooming out from a single plugin call, here is the full lifecycle a container’s network identity moves through, from birth to death, in a CNI-managed environment such as Kubernetes.
sequenceDiagram
participant U as Operator/Scheduler
participant KB as Kubelet
participant CR as containerd
participant CNI as CNI Plugin
participant NS as Pod Netns
U->>KB: Schedule Pod on Node
KB->>CR: Create sandbox (pause container)
CR->>NS: Create empty network namespace
KB->>CNI: ADD (via CRI networking call)
CNI->>NS: Create veth pair, assign IP, set routes
CNI-->>KB: Return interface + IP JSON
KB->>CR: Start application containers in same netns
Note over NS: Pod is now reachable
U->>KB: Delete Pod
KB->>CNI: DEL
CNI->>NS: Remove veth, release IP, clean routes
KB->>CR: Destroy sandbox
Notice a subtle but important detail: all containers inside one Kubernetes pod share a single network namespace, created once by a special lightweight “pause” or “sandbox” container. CNI’s ADD is only called once per pod, not once per container inside it — this is why every container in a pod can reach each other over localhost and shares the same IP address. Docker, running standalone, has no equivalent “pod” concept by default — each container normally gets its own namespace unless you explicitly ask it to share one with --network container:<name>.
8Types of CNI Plugins and the Major Vendors
Not all CNI plugins do the same job. They generally fall into a few families, and production clusters often run several together.
Interface Plugins
Create the actual network interface: bridge, macvlan, ipvlan, ptp (point-to-point veth pair), host-device.
IPAM Plugins
Hand out IP addresses: host-local (simple local subnet allocation), dhcp, cloud-native options like AWS or Azure IPAM.
Meta / Chained Plugins
Add extra behavior on top of an existing interface: portmap (NAT port forwarding), bandwidth (traffic shaping), firewall, tuning (sysctl adjustments).
Full Networking Solutions
Complete, opinionated networking stacks that combine interface creation, IPAM, and policy in one product: Calico, Cilium, Flannel, Weave Net, AWS VPC CNI, Azure CNI.
| Plugin | Primary Focus | Notable Trait |
|---|---|---|
| Flannel | Simple overlay networking | Very easy to set up; minimal feature set, no native network policy |
| Calico | Networking + Network Policy | Uses BGP for routing; strong, fine-grained security policy engine |
| Cilium | eBPF-based networking | Bypasses traditional iptables, offers deep observability (Hubble) and L7-aware policy |
| Weave Net | Overlay networking | Automatic mesh discovery, encrypted traffic option, also ships a Docker plugin |
| AWS VPC CNI | Native cloud IP addressing | Assigns real VPC IPs directly to pods, avoiding double-NAT overhead |
Choosing among these plugins is like choosing a phone carrier. All of them get a call connected (basic connectivity), but they differ wildly in coverage strategy (overlay vs. native routing), whether they offer a security add-on (network policy), and how much visibility they give you into call quality (observability tooling).
9Using CNI Plugins Where Docker Is Involved: Practical Setups
Given everything above, here is how CNI shows up in real, working infrastructure that includes Docker somewhere in the stack.
Setup A — Kubernetes with containerd (No Docker Daemon at All)
This is the modern default. Nodes run containerd directly (no Docker Engine installed), the kubelet calls containerd through the CRI, and containerd invokes the configured CNI plugin from /opt/cni/bin using the config in /etc/cni/net.d/. “Docker” as a daemon is entirely absent; only the underlying OCI image format and containerd — both technologies Docker helped create — remain in the picture.
Setup B — Legacy Kubernetes with Docker + dockershim
On older clusters (pre-1.24) or systems that reintroduced Docker compatibility via cri-dockerd, Docker Engine still runs containers, but Kubernetes’ CNI networking logic operates independently of Docker’s own bridge network — Kubernetes disables Docker’s default bridge involvement for pod traffic and lets the CNI plugin own the pod’s interface entirely.
Setup C — Plain Docker Engine, CNM Only
A developer running docker compose up or docker run on a single machine is using CNM/libnetwork end to end: bridge, overlay, or macvlan drivers, no CNI binaries involved, no /etc/cni/net.d directory consulted.
Context
A team is deciding whether to run workloads on plain Docker/Swarm or migrate to Kubernetes with a CNI plugin.
Decision
If the workload needs multi-host scheduling, fine-grained network policy, or a large plugin ecosystem, adopt Kubernetes with a CNI plugin like Calico. If the workload is a small, single-host set of services, Docker’s native CNM is sufficient and avoids unnecessary operational complexity.
Consequences
Choosing CNI brings a steeper learning curve and more moving parts (binaries, config files, IPAM state) but unlocks portability across runtimes and a far richer security policy model than CNM offers out of the box.
10Advantages, Disadvantages, and Trade-offs
Advantages of CNI
- Vendor-neutral standard adopted across the whole cloud-native ecosystem
- Minimal interface (four commands) keeps plugins simple to build and audit
- Chaining lets you compose small, focused plugins instead of one giant one
- Huge existing ecosystem: Calico, Cilium, Flannel, cloud-provider plugins
- Decouples the runtime from the networking implementation entirely
Trade-offs and Costs
- Not natively understood by plain Docker Engine — extra layers needed
- Operational surface grows: binaries, JSON configs, IPAM state to manage
- Different plugins have very different performance and feature profiles, adding a real evaluation burden
- Debugging spans multiple processes and namespaces, raising the troubleshooting bar
- Version skew between plugin and runtime CNI spec support can cause subtle breakage
11Security Considerations
Because a CNI plugin runs with elevated privileges — it manipulates network namespaces and host routing tables — its security posture matters as much as any other cluster component. Several concerns deserve attention.
Privilege scope. CNI plugins typically need CAP_NET_ADMIN and similar Linux capabilities to create interfaces and modify routes. A compromised or malicious plugin binary effectively has host-level networking control, so plugin binaries should only ever be sourced from trusted, verified distributions and pinned to specific, checksummed versions.
Network policy enforcement. Plain connectivity plugins like early Flannel provide no isolation between pods by default — any pod can talk to any other pod. Plugins such as Calico and Cilium add a NetworkPolicy engine, letting operators define allow-lists of which pods, namespaces, or IP ranges may communicate, which is the container-world equivalent of firewall rules and is essential for any multi-tenant or regulated environment.
Encryption in transit. Overlay networks send container traffic across regular host networking, which may traverse untrusted segments. Options like WireGuard-based encryption (available in Calico and Cilium) or Weave Net’s built-in encrypted mesh protect data as it crosses between hosts.
Teams frequently install a CNI plugin purely for connectivity and assume network segmentation “just works.” Without an explicit NetworkPolicy resource, most plugins default to fully open pod-to-pod traffic — the isolation has to be deliberately configured, not assumed.
“How would you restrict a payment-processing pod so only the checkout service can reach it?” — Strong answer: deploy a CNI plugin that supports NetworkPolicy (e.g. Calico), then write a policy selecting the payment pod’s labels as the target and the checkout service’s labels as the only allowed ingress source, denying all other traffic by default.
12Monitoring, Logging, and Metrics
Because CNI plugins run as short-lived, one-shot processes, traditional “check if the daemon is alive” monitoring does not directly apply — the plugin binary itself no longer exists moments after it runs. Observability instead focuses on three layers.
Invocation Logs
The container runtime and kubelet log every CNI ADD/DEL call, including failures, timing, and the JSON result — the first place to look when a pod is stuck in “ContainerCreating.”
Control-Plane Metrics
Full solutions like Calico and Cilium run a persistent agent per node (separate from the CNI binary itself) that exposes Prometheus metrics: interface counts, policy evaluation latency, dropped packet counts.
Data-Plane Visibility
Tools like Cilium’s Hubble give live, per-flow visibility into which pods are talking to which, at which protocol and port, invaluable for both debugging and security auditing.
A healthy operational habit is to track pod network setup latency as a first-class SLO-adjacent metric — a rising trend in ADD call duration is often the earliest signal of IPAM exhaustion (running out of assignable addresses in a subnet) long before pods start failing outright.
13Design Patterns and Anti-patterns
Good Patterns
- Chain a minimal main plugin with a dedicated IPAM plugin rather than one monolithic plugin
- Pin exact plugin binary versions in infrastructure-as-code, never “latest”
- Default-deny NetworkPolicy, then explicitly allow required paths
- Separate the pod CIDR range clearly from node and service CIDR ranges to avoid overlap
Anti-patterns
- Running two full CNI networking solutions on the same cluster simultaneously (route conflicts, duplicate IPAM)
- Manually editing
/etc/cni/net.dconfigs by hand on live nodes instead of through configuration management - Choosing an overlay-heavy plugin for a latency-critical workload without benchmarking native-routing alternatives
- Ignoring MTU mismatches between overlay tunnels and the underlying physical network, causing silent packet fragmentation
14Best Practices and Common Mistakes
Plan your IP space before you deploy. Pod CIDR, service CIDR, and node CIDR ranges must never overlap with each other or with any existing corporate network the cluster needs to reach — this single planning step prevents the majority of “mysteriously unreachable” incidents reported by teams new to CNI.
Match the plugin to the environment, not the hype. A cloud-native IPAM plugin that assigns real VPC addresses (like AWS VPC CNI) removes a layer of NAT overhead on cloud infrastructure but can exhaust the available IP pool faster on smaller subnets; an overlay plugin like Flannel is simpler to reason about but adds encapsulation overhead. Benchmark under realistic load rather than assuming.
Keep CNI binaries and cluster components in version lockstep. The CNI spec itself has evolved (notably from 0.x to 1.0.0), and mismatched plugin/runtime versions have historically caused ADD calls to silently fail or misinterpret configuration fields.
Assuming a “network policy” written for one plugin automatically works after switching to another. Some plugins implement the standard Kubernetes NetworkPolicy resource plus their own extended CRDs (like Calico’s GlobalNetworkPolicy) — a migration between plugins should always include a full policy audit.
15Real-World and Industry Examples
Large-Scale SaaS Platforms on Cilium
Organizations running very high pod density per node have increasingly adopted Cilium specifically because its eBPF-based data path avoids the iptables rule-count explosion that traditional netfilter-based plugins suffer from at scale, keeping packet processing latency predictable as the cluster grows into the thousands of nodes.
Regulated Industries and Calico Network Policy
Financial services and healthcare platforms commonly standardize on Calico specifically for its mature, fine-grained NetworkPolicy and GlobalNetworkPolicy engine, which lets security teams express compliance-driven segmentation (e.g. “only the audit-log service may reach the database tier”) as version-controlled, reviewable policy objects.
Cloud-Native Startups on Managed Kubernetes
Teams on managed services like Amazon EKS or Azure AKS frequently rely on the cloud provider’s own CNI plugin (AWS VPC CNI, Azure CNI) specifically because it assigns pods real addresses from the cloud’s native IP space, simplifying integration with existing VPC-based security groups, load balancers, and on-prem connectivity that predates the Kubernetes adoption.
16Frequently Asked Questions
17Summary and Key Takeaways
Key Takeaways
- CNI is a specification, not a product — a small, four-verb contract (ADD, DEL, CHECK, VERSION) that lets any container runtime work with any compatible networking plugin.
- Docker Engine itself uses CNM/libnetwork, its own separate networking model, not CNI, for standard
docker runanddocker networkworkflows. - containerd — the engine underneath Docker — is CNI-native, which is why CNI appears in the Docker “family” even though the Docker daemon layer does not speak it directly.
- Kubernetes is the primary consumer of CNI in practice, invoking plugins through containerd or CRI-O via the Container Runtime Interface.
- Plugins are commonly chained: a main plugin for interface creation, an IPAM plugin for address allocation, and meta plugins for extras like port mapping.
- Security is not automatic — network segmentation between pods requires explicit NetworkPolicy configuration on a plugin that supports it, such as Calico or Cilium.
- Choose deliberatelyoverlay simplicity (Flannel), policy depth (Calico), eBPF performance and observability (Cilium), or native cloud IP integration (AWS/Azure CNI) — the right choice depends on scale, security requirements, and infrastructure.