Build1 publisher3 min readPublished
Serving dockerd from WSL2 on docker.exe's own named pipe keeps Compose and Testcontainers working
Docker's engine is Apache-2.0 and the paid part is the application around it, so a team that cancels only has to serve the Engine API on the pipe docker.exe already opens. Microsoft's own wslc starts a real dockerd and leaves that endpoint closed.
The Engineer · Build desk

What happened
- Docker Desktop has required a paid subscription at companies past a certain size since 2021, and developers at those companies have been told to stop using it.
- Skrog, the project the post describes, serves that endpoint from Windows and can front Microsoft's engine instead of its own with skrog install --engine wslc.
- A 276 ms list images call turned out to be dockerd computing image sizes, reproduced at 160 ms per request against the engine's own unix socket with the bridge removed.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Standardising on wslc as shipped means standardising on build and run: Compose, Testcontainers, Dev Containers, buildx, act, Dagger and anything mounting docker.sock have nothing to connect to.
- cost Serving your own engine moves the lifecycle problem onto your platform team: a supervisor at logon, a watcher for the supervisor, and an idle timeout somebody has to set.
- decision The endpoint and the runtime become separate procurement questions, so a shop that wants Microsoft to own the engine can have that and still keep an API its tooling can reach.
`docker.exe` does not know what is on the other end of `\\.\pipe\docker_engine`. It opens the pipe and speaks the Engine API. Compose, buildx, Testcontainers and Dev Containers use the same API over the same pipe, so a `dockerd` hosted in a WSL2 distro behind that endpoint needs no per-tool change [5]. The Apache-2.0 licence on the engine is what makes serving it yourself a licensing non-event [3].
Microsoft's own runtime gets close and then stops one flag short. The author of the post checked what `wslc` starts and found stock `dockerd` on a unix socket inside the session VM, a genuine Moby engine and not a reimplementation [9]. It is launched with no `-H` flag: no named pipe, no TCP port, no inbound route from Windows [10]. "The engine is complete and unreachable," the author wrote [11]. The `wslc` CLI mimics docker for `build` and `run` [12].
`wslc` should win most of the control plane and does. It goes COM-to-VM with nothing in the path, while Skrog serves a named pipe and relays over vsock, which the author puts at 20 to 40 ms of the gap [17]. Actually running a container is a tie [18].
The interesting number is 276 ms for `list images` [19]. The author benchmarked both on one host because, as the post puts it, "we're fast" is not a claim, it is a vibe [23]. First hypothesis was payload size: `/images/json` returns 7,838 bytes on that machine against 7,052 for `/containers/json` [20], about 11 percent more bytes for 3.3 times the time [27]. Second hypothesis was the bridge's own reference parser, which sits in that path for admission control, so the author cut the bridge out and hit the engine's unix socket from inside a container, 20 requests each: 48 ms per call for containers, 160 ms for images [21]. Same ratio with no Windows, no pipe and no Skrog code in the path. The cost is `dockerd` computing image sizes, and the author says it would cost the same under Docker Desktop [22]. Subtracting the engine-side 160 ms from the 276 ms wall clock leaves about 116 ms for CLI process spawn and the relay [29].
For those timings to transfer, your host has to look like the author's: Windows 10 Pro 22H2 build 19045, WSL 2.9.11, 4 vCPU and 7.8 GB visible to each VM, engine 29.8.1, `/mnt/c` on 9p [15]. Control-plane numbers are wall-clock around each shipped CLI and include process spawn on both sides; the filesystem numbers were timed inside the container and do not [16]. If the cost is per-image size computation [22], the images figure only travels to a workstation with a comparable image count. All of it comes from one post by Skrog's own author, on one machine [30].
Lifecycle is where these setups usually fail. WSL2's VM disappears: `wsl --shutdown` kills it, sleep and resume break it, the engine crashes [24]. Skrog answers with a supervisor that starts at logon and watches the engine, plus a launcher that restarts the supervisor in about a second if it dies [25]. The idle-RAM complaint about `vmmem` holding a gigabyte gets a config line, `skrog config set idle-timeout 30m`, which stops a quiet engine and cold-starts it on your next docker command [26]. The documented install fetches a PowerShell script over HTTPS and pipes it into `iex` [7].
In my view the bridge is worth its 20 to 40 ms, because I want Testcontainers to be able to open a socket and that overhead is invisible in an interactive session. A build script that shells out to docker a thousand times pays 20 to 40 seconds of it [31].
What to watch
- Whether wslc's GA build opens a Windows-reachable endpoint, which would leave a third-party bridge arguing only lifecycle and idle timeout.