Testcontainers and wslc
Testcontainers does not work with wslc, and no setting will make it work.
If you uninstalled Docker Desktop expecting WSL containers to cover your
integration tests, that is the one workflow it cannot replace. The fix is not a wslc flag — it is
running Docker Engine inside WSL2 alongside it.
This page explains the actual incompatibility, since the error messages point at the wrong thing, then gives the supported setup that keeps you off a Docker Desktop licence.
On this page
Why it cannot work
Testcontainers is a client for the Docker Engine HTTP API. On startup it runs a
discovery sequence — check DOCKER_HOST, check the default Unix socket, check Docker
contexts, try a /_ping request — and it needs one of those to answer. Everything it
does afterwards (create container, wait for a port, stream logs, tear down via the Ryuk reaper
container) is API calls against that endpoint.
wslc is daemonless. There is no dockerd, no socket, and no HTTP API.
The CLI talks to a host service that starts a utility VM on demand, over a private channel that is
not the Docker API and is not documented as a public interface. This is the same design decision
that gives wslc near-zero idle memory use, and it is why the compatibility is not a gap someone
forgot to fill.
Setting DOCKER_HOST to anything wslc-related will not help. There is
no endpoint to point it at. Similarly, aliasing docker to wslc does not
work: Testcontainers does not shell out to the docker binary, it opens a socket.
That distinction is why VS Code Dev Containers
can use wslc while Testcontainers cannot — the extension invokes a CLI, Testcontainers
calls an API.
Confirming this is your problem
Typical symptoms are Could not find a valid Docker environment,
DockerClientProviderStrategy failed, or a hang followed by a timeout. Verify the
cause rather than guessing:
# Testcontainers looks for a Docker API. wslc provides none of these:
docker context ls # command does not exist in wslc
echo $DOCKER_HOST # empty
ls -la /var/run/docker.sock # No such file or directory
curl --unix-socket /var/run/docker.sock http://localhost/_ping # fails
# wslc works, but through its own private channel:
wslc container list # fine
wslc --version # fine
If wslc container list succeeds while every socket probe fails, the diagnosis is
confirmed: you have a working container runtime that is invisible to Testcontainers.
What else this affects
Anything that speaks the Docker API rather than the Docker CLI has the same problem. This is the broader category worth knowing before you plan a migration:
| Tool | Talks via | wslc | Notes |
|---|---|---|---|
| Testcontainers (all languages) | Engine API | No | Java, Go, .NET, Node, Python, Rust bindings alike. |
| Docker-in-Docker sidecars | Mounted socket | No | Nothing to bind-mount. |
| Portainer, Lazydocker | Engine API | No | Management UIs cannot connect. |
| LocalStack (docker mode) | Engine API | No | Spawns containers itself. |
| Act (local GitHub Actions) | Engine API | No | — |
| VS Code Dev Containers | CLI binary | Yes | Set dockerPath to wslc. |
Shell scripts calling docker | CLI binary | Mostly | Rename verbs; see the converter. |
The pattern is consistent: CLI-driven tooling ports to wslc, API-driven tooling does not. Use that as the test when evaluating anything not on this list.
The supported setup: Docker Engine in WSL2
Install Docker Engine inside an ordinary WSL2 distribution. It is open source, carries no Docker Desktop licence requirement, and gives Testcontainers exactly the API it looks for.
# Inside a normal WSL2 distro (Ubuntu), not the wslc runtime.
# 1. install Docker Engine — free, no Docker Desktop licence
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 2. enable systemd so the daemon survives shell exit
# /etc/wsl.conf:
# [boot]
# systemd=true
# then, from PowerShell: wsl --shutdown
# 3. run without sudo
sudo usermod -aG docker $USER # log out and back in
# 4. verify Testcontainers can see a daemon
docker info
docker run --rm hello-world
After this, Testcontainers discovers the daemon with no configuration. The
systemd=true step matters more than it looks — without it the daemon does not survive
your shell exiting, and you get intermittent failures that look like flaky tests.
Running both side by side
wslc and Docker Engine can both be installed. They use separate utility VMs and separate image stores, so there is no conflict beyond disk usage:
| Task | Runtime | Why |
|---|---|---|
| Ad-hoc containers, quick services | wslc | No daemon, near-zero idle memory, instant start |
| VS Code dev containers | wslc | Extension drives a CLI, not an API |
| Integration tests (Testcontainers) | Docker Engine | Requires the Engine API |
| Compose stacks | Docker Engine | wslc has no Compose runtime |
| Management UI | Docker Engine | Portainer needs the API |
The real cost is duplicated images: a postgres:16-alpine pulled by wslc is not visible
to Docker and vice versa. Budget the disk and move on.
CI is unaffected
wslc is a Windows local-development runtime. CI runners are Linux with a real Docker daemon, so nothing in your pipeline changes:
# GitHub Actions: ubuntu-latest already has a Docker daemon.
# Nothing about wslc changes CI — it is a local-development runtime.
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: '21' }
- run: ./gradlew test # Testcontainers works, daemon is present This is worth stating because the natural worry is that dropping Docker Desktop locally will break the build. It does not. The split is only on developer machines.
FAQ
- Does Testcontainers work with WSL containers (wslc)?
- No. Testcontainers discovers and drives a container runtime through the Docker Engine HTTP API, usually via DOCKER_HOST. wslc is daemonless and exposes no Docker-compatible socket or API, so Testcontainers cannot detect it. There is no configuration flag that fixes this.
- How do I run Testcontainers on Windows without Docker Desktop?
- Install Docker Engine (docker-ce) or Podman inside a normal WSL2 distribution and point DOCKER_HOST at it. This is free, needs no Docker Desktop licence, and gives Testcontainers the Docker API it requires. wslc is not part of this path.
- Why does wslc not expose a Docker socket?
- It is daemonless by design. Instead of a long-running dockerd listening on a socket, the host service starts a utility VM on demand and the CLI talks to it over a private channel. That is why idle memory use is near zero, and also why socket-dependent tooling cannot attach.
- Can I use wslc for development and Docker for tests?
- Yes, and it is a reasonable setup. The two runtimes coexist without conflict since they use separate VMs and image stores. Use wslc for ad-hoc containers and dev containers, and Docker Engine inside WSL2 for the integration test suite. The cost is pulling images twice.