Skip to content

Feature: add a wslc:// transport for WSL Containers (Microsoft WSL 2.9+) #2658

Description

@DavidTavoularis

Feature request

Add support for talking to the Docker daemon that ships inside WSL Containers (wslc) on Windows (Microsoft WSL 2.9+): a wslc:// transport, plus the small amount of lifecycle glue needed for published ports, host bind mounts and user-defined networks to actually work.

Motivation

WSL Containers runs dockerd inside a dedicated lightweight VM. Unlike Docker Desktop or Podman it exposes no Windows named pipe and no TCP port on the host — the only host-visible channel to the daemon is a stdio bridge:
wslc.exe system session run docker system dial-stdio

This is the same shape as the existing ssh:// transport (a child process whose stdin/stdout are the daemon socket). Today docker-java (and Testcontainers on top of it) has no way to reach a wslc-only daemon.

Proposed change

1. Transport (docker-java-transport + -transport-httpclient5) — mirror unix/npipe, dependency-free:

  • new WslcSocket extends Socket, streams backed by the wslc … dial-stdio child process (this module is JNA-only / no SLF4J, so it uses java.util.loggingno new dependency);
  • add case "wslc": to the scheme switch in ApacheDockerHttpClientImpl and a new WslcSocket(dockerHostPath) branch. Hijacked exec/attach/log streams work for free (they run at the HttpClient5 socket level).

2. Lifecycle integration (docker-java-core) — wslc's Windows integration (the 127.0.0.1 port relay and Windows-path bind mounts) is wired by the wslc control plane only when a container is created/started through the native wslc CLI, not through the Docker API on the same daemon (see microsoft/WSL #40957). A container created via the Docker API reaches the same dockerd but its published ports aren't reachable from Windows and host binds are rejected.
To bridge that, a WslcLifecycleDockerHttpClient (a DockerHttpClient decorator, jackson-only, no new dependency) reconciles just the affected calls with the wslc CLI and delegates everything else unchanged:

  • POST /containers/createwslc create
  • POST /containers/{id}/startwslc start (this is what wires relay + bind)
  • POST /networks/createwslc network create (wslc --network only sees wslc-created networks; the id→name is resolved via a Docker-API inspect)
  • GET /containers/{id}/json → the daemon's inspect with NetworkSettings.Ports overridden from wslc list (for a randomly published port the daemon records the create-time port but the start relay binds a different one)

Activation is transparent: DockerClientImpl.getInstance(config, httpClient) wraps the client in this decorator only when the host scheme is wslc. So it never affects unix/npipe/tcp users, and every docker-java consumer picks it up with no extra wiring.

3. Auto-detection (docker-java-core / DefaultDockerClientConfig)
— when DOCKER_HOST is unset, fall back to wslc://localhost on Windows only if the //./pipe/docker_engine pipe is absent AND wslc is available (wslc version exit 0, overridable via WSLC_EXECUTABLE) — so existing Docker Desktop / Podman setups keep winning.

Using it with Testcontainers

Testcontainers shades docker-java-core but consumes docker-java-api / -transport-zerodep as normal dependencies. So with just this PR:

  • set DOCKER_HOST=wslc://localhost explicitly, and
  • rebuild Testcontainers against the updated (shaded) docker-java so the new getInstance activation is re-shaded into its jar (a plain version bump is not enough — core is shaded).

That combination is verified working end-to-end: a real Testcontainers/Cucumber integration-test suite goes fully green over wslc (containers create/start via the CLI, ports reachable from Windows, custom networks resolved).

The one piece that can't live in docker-java is automatic host selection when DOCKER_HOST is unset — that requires a class implementing Testcontainers' DockerClientProviderStrategy, which docker-java can't provide without depending on Testcontainers. I'll open a separate PR against testcontainers-java to add that automatic fallback to wslc:// when it's the only Docker environment present on Windows, so users won't need to set DOCKER_HOST at all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions