Skip to content

fix: do not require CNI plugins for compose projects that avoid CNI - #5132

Open
ravi-arnan wants to merge 1 commit into
containerd:mainfrom
ravi-arnan:fix-compose-cni-4461
Open

fix: do not require CNI plugins for compose projects that avoid CNI#5132
ravi-arnan wants to merge 1 commit into
containerd:mainfrom
ravi-arnan:fix-compose-cni-4461

Conversation

@ravi-arnan

Copy link
Copy Markdown

Fixes #4461

Problem

On a host with no CNI plugins installed, every nerdctl compose command fails, even when no
service in the project uses CNI networking:

services:
  probe:
    image: alpine
    network_mode: host
    command: echo OK
FATA[0000] failed to create default network: needs CNI plugin "bridge" to be installed in
CNI_PATH ("/opt/cni/bin"), see https://github.com/containernetworking/plugins/releases

Docker starts this project fine, and so does nerdctl run --net host on current main.

Cause

compose.New passed netutil.WithDefaultNetwork(...) to NewCNIEnv, which creates nerdctl's
default bridge network as a side effect of constructing the environment. That happens before any
service has been parsed, so it applies to every compose invocation regardless of what the services
actually ask for.

The default network is not needed there. The only consumer of that CNIEnv is NetworkExists, and
both call sites (up_network.go, down.go) invoke it exclusively with project-scoped names such as
myproject_default. External networks return before reaching it. Nothing in the compose path reads
the default bridge.

Services that genuinely use the default bridge are unaffected: compose shells out to nerdctl run,
and that path ensures the default network through cniNetworkManager when it is actually needed.

Fix

Drop WithDefaultNetwork from the CNIEnv built in compose.New. One-line change plus a comment
explaining why the eager creation is deliberately absent.

Verification

Built from this branch and compared against an unpatched build of the same commit, on Zorin OS 18
(kernel 6.17), containerd 2.3.3, runc 1.4.3. --cni-path and --cni-netconfpath were used to
simulate a host without plugins, so neither /opt/cni/bin nor /etc/cni/net.d was involved.

case unpatched patched
A. compose up, network_mode: host, empty CNI path failed to create default network, service never ran service ran
B. compose up, ordinary project on its _default bridge network, real plugins service ran service ran
C. compose up, network_mode: bridge, real plugins, empty netconf dir n/a service ran, and nerdctl-bridge.conflist was created on demand by the run path

Case C is the one the change could plausibly break, and it confirms the default network is still
created when a service actually asks for the bridge.

The TestComposeUp, TestComposeDown and TestComposeCreate selection was also run against both
builds from identical host state (leftover nerdctl bridges deleted between runs, since a stale
bridge holding 10.4.0.0/24 makes the next run fail the default network overlap check):

unpatched : 51 pass, 4 fail, 3 skip
patched   : 55 pass, 0 fail, 3 skip

Of the four failures on the unpatched build, one is the new regression test below, and the other
three are TestComposeCreatePull and two of its subtests failing on an IPv6 timeout reaching
ghcr.io. That one is unrelated to this change and passes on the unpatched build when the registry
is reachable. No test regressed.

For the plain run half of the issue report, both nerdctl run --net host and
nerdctl run --net none already succeed on unpatched main with no CNI plugins present, so that
half appears to have been fixed previously. Details are in the issue thread.

Test

TestComposeUpNetworkModeHostWithoutCNIPlugins runs compose up -d on a network_mode: host
project with --cni-path and --cni-netconfpath pointing at empty directories. It fails on
unpatched code with the error above and passes with the fix.

Note the test needs both flags. Pointing only --cni-path at an empty directory is not enough,
because a default network config left in the real netconf dir by an earlier test would satisfy
GetDefaultNetworkConfig and the creation attempt would never happen.

compose.New unconditionally passed netutil.WithDefaultNetwork to
NewCNIEnv, so every compose command created nerdctl's default bridge
network before parsing a single service. On a host without the CNI
plugins installed this failed outright, even for projects whose services
all use network_mode: host or none and so never touch CNI.

The default network is not needed there. NetworkExists, the only consumer
of the CNIEnv built in compose.New, is called exclusively with
project-scoped network names, and external networks return before
reaching it. Services that do attach to the default bridge still get it
created on demand, because compose shells out to `nerdctl run`, which
ensures the default network via cniNetworkManager.

Fixes containerd#4461

Signed-off-by: Ravi Arnan <raviarnankeren@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nerdctl requires CNI to be installed when running containers with network_mode host

1 participant