Symptom
A compose-source devcontainer whose service declares
services:
app:
network_mode: host
is passed straight through when the engine runs inside a Kubernetes pod. The
container joins the pod's network namespace — the one the engine does not
exclusively own — where it shares every listening socket with the sidecars
already there. Any port the service binds collides with whatever holds it,
and the failure surfaces as a raw bind: address already in use from the
service's own process, with nothing pointing at the actual conflict.
This is the sibling of #135, filed from its "Not in scope" section.
Cause
The native orchestrator resolves the namespace mode and hands it to the
backend verbatim (compose/orchestrator.go, resolveNamespaceMode — "" /
host / none / container:<id> pass through unchanged, by design, because
that is correct compose behaviour when the daemon's host is the user's
machine).
EngineOptions.DisableHostPortPublishing (#135) tells the engine that
assumption does not hold, and drops the host side of ports: on that basis.
It does not touch network_mode: host, because the two cases do not have the
same answer:
- Dropping a
ports: publish is semantically safe — service-to-service
traffic goes over the compose network by service name, so nothing the
project relies on changes.
- Silently moving a
network_mode: host service onto the project's bridge
network is not safe. The service asked to be on the host's namespace;
putting it somewhere else changes what it can reach and who can reach it,
in ways the compose file cannot express and the user did not ask for.
So the option currently covers one half of the collision and leaves the other
half to fail at boot.
Suggested fix
Refuse it. Under the same option, a service declaring
network_mode: host should be rejected at plan time with a typed error that
names the service and says why, rather than started into a namespace it will
fight over.
Plan-time is the right gate: compose.Plan.Validate already refuses the
compose features the orchestrator will not run (compose/plan.go,
compose.UnsupportedFeatureOnBackendError and neighbours), and refusing there
costs the user nothing — no image is pulled, no sidecar is built, nothing is
created.
That needs the option to reach Plan.Validate, which today only sees
runtime.Capabilities. The shape is open: a field on Plan, or an
Orchestrator field, or folding it into the capability set. Worth deciding
against the case where more environment-derived refusals follow, rather than
in isolation.
Not in scope
Symptom
A compose-source devcontainer whose service declares
is passed straight through when the engine runs inside a Kubernetes pod. The
container joins the pod's network namespace — the one the engine does not
exclusively own — where it shares every listening socket with the sidecars
already there. Any port the service binds collides with whatever holds it,
and the failure surfaces as a raw
bind: address already in usefrom theservice's own process, with nothing pointing at the actual conflict.
This is the sibling of #135, filed from its "Not in scope" section.
Cause
The native orchestrator resolves the namespace mode and hands it to the
backend verbatim (
compose/orchestrator.go,resolveNamespaceMode— "" /host/none/container:<id>pass through unchanged, by design, becausethat is correct compose behaviour when the daemon's host is the user's
machine).
EngineOptions.DisableHostPortPublishing(#135) tells the engine thatassumption does not hold, and drops the host side of
ports:on that basis.It does not touch
network_mode: host, because the two cases do not have thesame answer:
ports:publish is semantically safe — service-to-servicetraffic goes over the compose network by service name, so nothing the
project relies on changes.
network_mode: hostservice onto the project's bridgenetwork is not safe. The service asked to be on the host's namespace;
putting it somewhere else changes what it can reach and who can reach it,
in ways the compose file cannot express and the user did not ask for.
So the option currently covers one half of the collision and leaves the other
half to fail at boot.
Suggested fix
Refuse it. Under the same option, a service declaring
network_mode: hostshould be rejected at plan time with a typed error thatnames the service and says why, rather than started into a namespace it will
fight over.
Plan-time is the right gate:
compose.Plan.Validatealready refuses thecompose features the orchestrator will not run (
compose/plan.go,compose.UnsupportedFeatureOnBackendErrorand neighbours), and refusing therecosts the user nothing — no image is pulled, no sidecar is built, nothing is
created.
That needs the option to reach
Plan.Validate, which today only seesruntime.Capabilities. The shape is open: a field onPlan, or anOrchestratorfield, or folding it into the capability set. Worth decidingagainst the case where more environment-derived refusals follow, rather than
in isolation.
Not in scope
ports:, which engine: option to skip compose host port publishing when the host netns is shared #135 covers.pid:/ipc:set tohost. Same pass-through, but sharing a PID or IPCnamespace with sidecars is a visibility and isolation question, not a port
collision, and it does not fail at boot. Worth a separate look.