Docker provider via WSL
This tutorial runs a Docker engine inside WSL2 and connects Devsy, running on Windows, to it over SSH. Docker runs inside WSL, while Devsy and the editor run on Windows. Keep local project files on a Windows drive so Devsy can translate their mount paths into WSL paths.
Install Docker in WSL
Enable WSL2
In PowerShell as Administrator, skip this step if WSL2 is already installed:
wsl --installThis enables the required features and installs a default Linux distribution. Without wsl --install, enable both the Windows Subsystem for Linux and the Virtual Machine Platform features by hand, restart, and run wsl --set-default-version 2. Without the Virtual Machine Platform, WSL falls back to WSL1.
To install a specific distribution, run wsl --install Ubuntu-24.04 and set a username and password. Open its shell with wsl.
The steps below use systemctl, which needs systemd inside WSL. Distributions installed through wsl --install enable it by default. If it is missing, add this to /etc/wsl.conf, then run wsl --shutdown from PowerShell and start wsl again:
[boot]
systemd=trueInstall Docker Engine
Follow the official Docker Engine installation for Ubuntu inside WSL. Then add your user to the docker group and start the services:
sudo usermod -aG docker $USER
sudo systemctl enable --now docker.service containerd.serviceBehind a corporate proxy, also configure the Docker daemon proxy.
Install an SSH server
The Windows Docker client reaches the daemon over SSH, not an open TCP port:
sudo apt-get update
sudo apt-get install -y openssh-server
sudo systemctl enable --now ssh.serviceWSL2 forwards localhost to Windows, so the server is reachable there. To avoid password prompts, run ssh-keygen on Windows and append the public key to ~/.ssh/authorized_keys in WSL.
Connect Windows to the daemon
Download the Docker CLI from the Docker static binaries for Windows, extract it, and add its folder to PATH. Then create a context that uses SSH. Replace <wsl-username> with your WSL username:
docker context create lin --docker "host=ssh://<wsl-username>@localhost"
docker context use lin
docker run hello-worldThe first connection asks you to accept the SSH host key.
Connect Devsy
Add the Docker provider and point it at the WSL daemon:
devsy provider add docker -o DOCKER_HOST=ssh://<wsl-username>@localhost -o DOCKER_PATH=dockerIf the provider already exists, use devsy provider set docker -o ... with the same options. Check the result with devsy provider get docker.
DOCKER_HOST accepts anything the Docker CLI accepts.
How Devsy mounts project files
When DOCKER_HOST is not a local socket, Devsy converts Windows drive paths in mounts to their WSL form. A project at C:\Users\me\repo is mounted as /mnt/c/Users/me/repo. Keep project files on a Windows drive. Paths under \\wsl.localhost\... are not supported for mounts.
Connect your editor
Create the workspace. Devsy adds an SSH config entry named <workspace-name>.devsy:
devsy workspace upIn VS Code or VSCodium with the Open Remote - SSH extension, choose Open Folder in Remote... and select that host. Devsy tunnels the session into the dev container, so no jump host setup is needed.
The tunnel reuses your local SSH keys and agent. If the editor log shows write EPIPE, run devsy workspace up with DEVSY_DEBUG=true. The real cause, such as the WSL SSH server not running or an untrusted host key, is otherwise hidden behind that generic error.
Next steps
See Create a Workspace.
Experimental WSLg display discovery
Windows Devsy can discover display connection values from an explicitly selected WSL2 distribution and non-root user. This is opt-in and requires the local docker provider with Docker Engine in that distribution, as configured above. Docker Desktop, Podman, remote providers and Devsy running inside WSL are outside this feature's scope. The Docker endpoint must reach the same distribution: its name or an SSH address alone does not establish that identity.
First check that a GUI application works directly in your distribution and that these commands from PowerShell return its display values. Replace the distribution and username:
wsl.exe --distribution Ubuntu-24.04 --user alice --exec /usr/bin/printenv DISPLAY
wsl.exe --distribution Ubuntu-24.04 --user alice --exec /usr/bin/printenv WAYLAND_DISPLAY
wsl.exe --distribution Ubuntu-24.04 --user alice --exec /usr/bin/printenv XDG_RUNTIME_DIRDevsy uses this fixed noninteractive probe, with a 30-second total deadline. It does not start a login shell or read shell profiles. If these commands cannot read the values used by your GUI application, this discovery method cannot support that setup.
Enable discovery in the owner-controlled Devsy context, using the same context as your workspace:
devsy context set -o WSLG_ENABLED=true -o WSLG_DISTRO=Ubuntu-24.04 -o WSLG_USER=aliceThe experimental v1 substitution interface exposes only:
| WSL value | Devsy substitution / Compose interpolation key |
|---|---|
DISPLAY | DEVSY_WSLG_DISPLAY |
WAYLAND_DISPLAY | DEVSY_WSLG_WAYLAND_DISPLAY |
XDG_RUNTIME_DIR | DEVSY_WSLG_XDG_RUNTIME_DIR |
Missing values stay absent, and present empty values stay empty. Discovery sets no container environment and adds no mounts. Explicitly map the desired values in your project. Audio, GPU, session bus, SSH sockets and WAYLAND_SOCKET file descriptors are not imported.
These names are reserved while discovery is enabled. Remove assignments to them from your Windows environment, --init-env, provider Docker environment and Compose interpolation .env files. Even empty assignments, or differently cased Windows names, are rejected. Ordinary DISPLAY remains user-controlled. Service env_file entries are container environment, separate from interpolation files. The separate COMPOSE_ENV_FILES environment override is unsupported while discovery is enabled; use the project .env files Devsy passes explicitly.
X11 configuration mechanics
The following examples show the environment and mount configuration. They require an image with an X11 client, a working WSLg server and a daemon-visible /tmp/.X11-unix/X0 socket. Hosted CI checks environment transport and socket access; a visible Windows window still needs acceptance testing on your machine. See Microsoft's WSLg container guidance for the underlying socket requirements.
For an image or Dockerfile devcontainer, map the discovered display and pass an explicit daemon-side bind using Docker runArgs:
{
"image": "your-image-with-an-x11-client",
"containerEnv": {
"DISPLAY": "${localEnv:DEVSY_WSLG_DISPLAY}"
},
"runArgs": [
"--mount",
"type=bind,source=/tmp/.X11-unix,target=/tmp/.X11-unix,readonly"
]
}runArgs sends this explicit Linux source directly to Docker's strict --mount validation. Devsy's general mounts property still checks bind sources on the Windows client; use the explicit Docker argument for this daemon-only socket path. A missing source fails instead of creating a directory.
For Compose, the namespaced values are available when Devsy loads the original Compose files and when the Compose executable processes them:
services:
dev:
image: your-image-with-an-x11-client
command: sleep infinity
environment:
DISPLAY: "${DEVSY_WSLG_DISPLAY:?Enable WSLg discovery for this context}"
volumes:
- type: bind
source: /tmp/.X11-unix
target: /tmp/.X11-unix
read_only: true
bind:
create_host_path: falseA read-only Unix socket mount still allows communication with its server. Trust projects that receive access to your GUI session. Devsy does not change X11 authorization, socket permissions, user IDs or host networking. remoteEnv only affects later remote sessions; it cannot repair original Compose interpolation or configure the container entrypoint.
Wayland and lifecycle
Wayland values can be referenced using the same explicit mapping, but XDG_RUNTIME_DIR is a host path with user ownership and permission requirements. Do not copy it blindly into the container or mount the whole host /run/user directory. Map only the daemon-visible Wayland socket into a container runtime directory owned by the intended user, or use an absolute socket path with a client that supports it. A native Wayland GUI recipe requires separate runtime acceptance; the X11 example does not prove it.
Each up/recreate/build captures fresh display values. Already running containers can be reused only when the recorded distribution, user and values match. Stopped WSLg containers, changed values, newly enabled discovery and opting out all require explicit devsy workspace up --recreate. After a WSL restart, use --recreate even if the strings match: socket identities may have changed.
Stop and delete never probe WSL. Compose containers record an allowlisted v2 invocation label containing the distribution/user and the three present display values, plus the original Compose paths needed for cleanup. Image containers carry the same display identity marker. These labels are visible to anyone who can inspect the Docker daemon; they contain host connection metadata, not credentials. Cleanup still needs a reachable Docker daemon and, for Compose, the original project files.
Disable discovery with:
devsy context set -o WSLG_ENABLED=falseRecreate existing WSLg workspaces after changing this setting. Disabled discovery preserves ordinary environment resolution and does not invoke WSL.