Skip to content

feat(engine): option to drop compose host port publishing - #137

Merged
bilby91 merged 1 commit into
mainfrom
feat/disable-host-port-publishing
Sep 16, 2026
Merged

bilby91 merged 1 commit into
mainfrom
feat/disable-host-port-publishing

Conversation

@bilby91

@bilby91 bilby91 commented Sep 16, 2026

Copy link
Copy Markdown
Member

Closes #135.

EngineOptions.DisableHostPortPublishing drops the host side of compose
ports: entries. Services still start and nothing else about them changes;
each dropped entry raises an engine.warn event
(compose_host_port_publish_skipped) naming the service and where it is
reachable, so the user can delete the entry rather than wonder why it did
nothing.

Design decisions

Naming: DisableHostPortPublishing, not SharedHostNamespace. The issue
left this open, with the why-name reading better if network_mode: host later
joins the same option. I drafted it that way first and it misread immediately —
an option called "shared host namespace" sounds like it puts the engine on
the host's namespace, which is the opposite of what it does. The what-name says
what changes; the why lives in the doc comment.

Dropped from the in-memory project, not inside the orchestrator's
portsOf.
The issue suggested portsOf returns nil. ConfigHash hashes the
project's ServiceConfig, so a drop there is invisible to the recreate check:
a container created before the option was turned on would keep its publishes
and be reused. Removing the entries from the project instead makes the hash
move, which is the correct behaviour and a deliberate recreate trigger (R3) —
turning the option on replaces the containers of services that declare
ports:. This also reuses the existing seam: compose.ApplyDropHostPorts
sits next to ApplyBuildOverride / ApplyRunOverride, whose whole job is
mutating the project before the orchestrator reads it, and keeps the
orchestrator free of a new option and a new warning channel (it has no event
surface today; the engine already has one).

Entries are removed whole, including ones with no published.
ports: ["8080"] binds an ephemeral host port — same namespace, same problem.

Shellout is refused, not silently ignored (R2). That path hands the user's
compose files to docker compose, which publishes ports: regardless, and
stripping them there would mean re-implementing merge / extends / override
resolution against the YAML. A compose-source Up under
ComposeBackendShellout with the option set returns an error naming the
backend that honours it. The failure the option exists to prevent is a
boot-time bind collision, which is worse to debug than a refusal at the top of
the call.

network_mode: host is not covered. Same collision, but a drop is not
available: silently moving the service to a bridge network changes
reachability. It wants a plan-time refusal, which needs the option to reach
Plan.Validate. Filed as #136 with the shape left open.

Tests

  • compose/apply_drop_host_ports_test.go — every entry on every service goes,
    in sorted service order (warning order must not depend on map iteration);
    published-less entries are dropped and rendered without a host side;
    nothing but ports: moves; and the ConfigHash pin for the paragraph above.
  • up_compose_host_ports_test.go — the consumer check (R1): the option reaches
    RunSpec.Ports at the backend boundary, not just the project; the warning
    fires and names the service, the entry, and the address to use instead; the
    publish still reaches the backend by default; shellout refuses. Reverting the
    call in upComposeNative fails the first of these.
  • test/integration/compose_host_port_publishing_test.go — a real daemon boots
    a project whose primary publishes a port with the option on, and the sidecar
    is still reachable by service name over the compose network. That is the half
    a fake cannot make, and it is the claim the whole design rests on. What the
    host binding does or does not do is asserted at the RunSpec boundary above;
    RunSpec.Ports → docker PortBindings translation is already covered in
    runtime/docker.

make lint test green. Integration tests were not run locally — overlayfs
is unusable in this devcontainer, so the pre-existing suite fails there the same
way (mount source: "overlay" … invalid argument); they run in CI.

🤖 Generated with Claude Code

EngineOptions.DisableHostPortPublishing drops the host side of compose
`ports:` entries. Services still start and nothing else about them
changes; each dropped entry raises an engine.warn event
(compose_host_port_publish_skipped) naming the service and where it is
reachable instead.

An embedder whose daemon "host" is a namespace shared with other
workloads — the engine running inside a Kubernetes pod next to sidecars
— cannot let a project bind there: a service publishing 8080 loses the
race against a sidecar already listening and the boot fails on a raw
`bind: address already in use`. Nothing in that shape consumes the
publish anyway, since service-to-service traffic goes over the compose
network by service name and an embedder forwarding a port out of the
namespace dials the container on that network — which is what makes
dropping it safe, and a silent default acceptable. It still warns, and
the message says where the service is reachable rather than only that
something was ignored.

The entries are removed from the in-memory project via a new
compose.ApplyDropHostPorts, alongside the existing Apply*Override
helpers, rather than inside the orchestrator's portsOf: ConfigHash
reads the project's ServiceConfig, so a container created before the
option was turned on drifts and is recreated instead of being reused
with its publishes intact.

Native backend only. The shellout path hands `ports:` to
`docker compose`, which publishes them regardless, and stripping them
there would mean re-implementing merge/extends/override resolution
against the YAML — so a compose-source Up under ComposeBackendShellout
is refused rather than run with the option silently ignored.

`network_mode: host` has the same collision but needs a refusal, not a
drop: moving the service to a bridge network would change reachability.
Filed as #136.

Closes #135

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bilby91

bilby91 commented Sep 16, 2026

Copy link
Copy Markdown
Member Author

@dap-code-review-by-crunchloop review

The check for this head reported "Review did not complete" — nothing was published for 41799df, so this is a re-request on the same commit rather than a response to findings.

@dap-code-review-by-crunchloop

Copy link
Copy Markdown

On it. A review of the current head is on its way.

@bilby91
bilby91 marked this pull request as draft September 16, 2026 17:05
@bilby91
bilby91 marked this pull request as ready for review September 16, 2026 17:05
@bilby91
bilby91 merged commit 3e895f3 into main Sep 16, 2026
9 of 10 checks passed
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.

engine: option to skip compose host port publishing when the host netns is shared

1 participant