How to install WSL containers on Windows 11
WSL containers is not a separate download. The runtime, the
wslc.exe CLI and the container host all ship inside the Windows Subsystem for Linux,
so installing it means moving WSL onto a pre-release channel and restarting the subsystem. The
whole process is two commands and takes under ten minutes on a machine that already has WSL 2.
On this page
Prerequisites
Four things have to be true before wslc will appear on your machine. Check all of
them first — every failure mode in the last section traces back to one of these.
| Requirement | How to check | Why |
|---|---|---|
| Windows 11 22H2+ or Windows Server 2025 | winver | The container host uses a Hyper-V utility VM path that does not exist on Windows 10. |
| x64 or ARM64 | echo $env:PROCESSOR_ARCHITECTURE | Both are supported. There is no x86 build. |
| Virtualisation available to the OS | (Get-CimInstance Win32_ComputerSystem).HypervisorPresent | Without SLAT/VT-x the utility VM cannot start. Nested virtualisation counts:
wslc runs fine on cloud VMs that expose it, such as GitHub Actions
windows-2025 runners. |
| Virtual Machine Platform feature on | Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform | Provides the host compute service WSL boots the container VM through. |
If the last one reports Disabled, enable it and reboot:
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart Store WSL vs inbox WSL. Windows ships an older in-box WSL component, and the
Microsoft Store ships the modern one. Only the Store build receives pre-release updates. Run
wsl --version: if you get an error or no version block, you are on the in-box
component and must install Windows Subsystem for Linux from the Store before continuing.
This is the single most common reason the install below appears to succeed but leaves no
wslc binary behind.
Install WSL containers in two commands
Open PowerShell as Administrator and run:
# 1. Move WSL onto the pre-release channel and update
wsl --update --pre-release
# 2. Restart the subsystem so the new container host loads
wsl --shutdown
The update pulls a WSL build of 2.9.3.0 or newer, which is the first version that
contains the container host. Expect a 150–250 MB download. The
wsl --shutdown is not optional: until every existing distribution and the old service
process are torn down, wslc will start and then immediately fail to reach its host.
Verify the install
wsl --version # expect 2.9.3.0 or newer
wslc version # CLI + runtime versions
wslc info # storage driver, VM state, resource limits wslc info is the useful one. It starts the utility VM on demand, so a clean response
proves the whole chain works: CLI → host service → Hyper-V VM → image store. If
wslc version answers but wslc info hangs, the VM is failing to boot and
the fix is almost always virtualisation or a conflicting hypervisor (see below).
How to check your WSL version
WSL containers needs WSL 2.9.3.0 or higher. To check what you have:
wsl --version
This prints the WSL version, kernel version, WSLg version, and Windows version in one block.
If the output says 2.9.4.0 or newer, you have wslc. If it says
2.7.x or older, you are on the stable channel and need the pre-release
build — follow the install steps above.
The wslc version command gives you the wslc CLI version specifically, which is
useful when you are debugging a command that behaves differently between builds:
wslc version
# wslc 2.9.4.0 Updating wslc
The pre-release channel updates more frequently than stable WSL. To get the latest wslc:
wsl --update --pre-release
wsl --shutdown
If you are already on pre-release and just want the latest build,
wsl --update stays on whichever channel you are on. To move from pre-release
back to stable, see the rollback section.
Run your first container
# smallest possible smoke test
wslc run --rm alpine echo "wslc works"
# pull, run detached, publish a port
wslc pull nginx:alpine
wslc run -d -p 8080:80 --name web nginx:alpine
# confirm it is up, then check from Windows
wslc container list
curl.exe http://localhost:8080
# clean up
wslc stop web
wslc container remove web
Note wslc container list rather than docker ps. wslc groups
listing commands under nouns; recent previews accept wslc ps as an alias but the noun
form is the stable one. The command converter handles this translation for whole
scripts at once.
The curl.exe http://localhost:8080 check matters more than it looks. wslc routes
container traffic through the Windows host network stack, so a published port is reachable on
localhost from Windows itself with no extra port-proxy step — and it inherits
your corporate VPN, firewall rules and proxy settings automatically. If this call fails while
wslc container list shows the container running, you have a Windows Firewall rule
problem, not a container problem.
Wire WSL containers into VS Code
The Dev Containers extension talks to Docker by default, but it only needs a CLI that speaks the
same verbs. Point it at wslc:
// settings.json
{
"dev.containers.dockerPath": "wslc"
}
Rebuild the container after changing this. Most single-container devcontainer.json
files work unchanged. Anything using dockerComposeFile will not — there is no
Compose runtime in wslc, which is covered in the
Compose migration guide.
Five errors that block most first runs
'wslc' is not recognized as an internal or external command
The binary was never laid down. Run wsl --version; anything below 2.9.3.0 means the
--pre-release update did not apply. The usual cause is the in-box WSL component being
active instead of the Store build — install WSL from the Microsoft Store, then repeat
wsl --update --pre-release. A new terminal is also required, since PATH is only read
at shell start.
The Virtual Machine Platform feature is not enabled
Run the dism.exe command from the prerequisites section and reboot. On machines
managed by enterprise policy this feature is sometimes blocked by Group Policy, in which case the
DISM call succeeds but the feature reverts after restart — check with your IT team rather
than fighting it locally.
Utility VM fails to start / wslc info hangs
Something else owns the hypervisor. VirtualBox below 7.0, VMware Workstation below 17, and some Android emulators and anti-cheat drivers take exclusive control of virtualisation extensions. Check the state of Hyper-V's own boot setting:
bcdedit /enum | findstr -i hypervisorlaunchtype
If it reports Off, re-enable it with
bcdedit /set hypervisorlaunchtype auto and reboot. Note that this will slow down or
break older VirtualBox/VMware installs — that trade-off is inherent to running any
Hyper-V-based container runtime, wslc included.
failed to resolve registry on the first pull
DNS inside the utility VM is not resolving. Because wslc inherits the host network configuration,
this usually means a VPN client is intercepting DNS. Reconnecting the VPN after
wsl --shutdown fixes most cases, since the VM picks up the current host resolver at
boot rather than watching for changes.
Permission denied on a bind-mounted Windows directory
Host directories are shared over VirtioFS, which maps Windows ACLs onto Linux UIDs approximately. A container process running as a non-root user often cannot write to a mounted Windows path. Two workarounds: run the container as root for local development, or keep write-heavy working directories inside a wslc-managed volume and mount the Windows path read-only.
# read-only host mount, writable named volume for output
wslc volume create build-out
wslc run --rm -v C:/src/myapp:/app:ro -v build-out:/app/dist myapp:dev Use forward slashes in Windows paths. Backslashes are interpreted as escape characters by the argument parser and produce confusing "no such file or directory" errors.
Run wslc in CI on a GitHub Actions runner
You do not need a local Windows box to exercise wslc. The hosted
windows-2025 runner is Windows Server 2025 on an Azure VM size that exposes
hardware nested virtualisation, so the Hyper-V utility VM boots normally. We confirmed this
end to end: pull an image, run a container, get output back.
| What we checked on the runner | Result |
|---|---|
HypervisorPresent | True |
vmcompute service | Running |
| Preinstalled WSL | 2.7.10.0 (stable, no wslc) |
After wsl --update --pre-release | 2.9.4.0, exit 0 |
wslc image ls | exit 0, HCS reachable |
wslc run a real container | exit 0, image pulled, output returned |
Two details cost us a failed run, and neither is documented anywhere:
-
The runner ships stable WSL, which has no
wslc. You must runwsl --update --pre-releasefirst. Unlike a physical machine, the runner needs no reboot for it to take effect. -
wslc.exelands inC:\Program Files\WSL\, and that directory is not on PATH in the already-running shell. Calling it by bare name fails withThe term 'wslc' is not recognized. Use the absolute path.
# .github/workflows/wslc.yml
name: wslc
on: [push, workflow_dispatch]
jobs:
containers:
runs-on: windows-2025
steps:
- name: Install wslc
shell: pwsh
run: |
wsl --update --pre-release
wsl --version
- name: Run a container
shell: pwsh
env:
WSL_UTF8: '1'
run: |
# not on PATH in this shell - call it by absolute path
$wslc = 'C:\Program Files\WSL\wslc.exe'
& $wslc version
& $wslc run --rm mcr.microsoft.com/cbl-mariner/base/core:2.0 echo hello Public repositories get unlimited free Actions minutes. Private repositories get 2,000 minutes a month, and Windows jobs bill at 2×, so budget for 1,000 real minutes. A run that updates WSL, pulls an image and starts a container takes about three minutes.
windows-11-arm looks like the obvious choice since wslc targets Windows 11,
but WSL does not work there at all: the Azure Arm64 VM sizes behind that runner do not
expose nested virtualisation. Use windows-2025.
How to roll back
The pre-release channel is sticky — later wsl --update calls stay on
pre-release until you tell them otherwise. To return to the stable channel:
wsl --update # stable channel
wsl --shutdown
This removes wslc and its container host. Images and named volumes stored in the wslc
image store are left on disk and will be picked up again if you re-enable the preview later, so a
rollback is not destructive. Your Linux distributions and their filesystems are untouched
throughout — the container host is a separate VM.
FAQ
- How do I install WSL containers on Windows 11?
- Run `wsl --update --pre-release` in an elevated PowerShell window, then `wsl --shutdown` to restart the subsystem. Verify with `wslc version`. WSL containers ships inside WSL itself, so there is no separate download or installer.
- What Windows version do I need for WSL containers?
- Windows 11 22H2 or later, or Windows Server 2025, on x64 or ARM64, with virtualisation available and the Virtual Machine Platform optional feature turned on. Windows 10 is not supported because the container host relies on a newer Hyper-V utility VM path. We verified Server 2025 by running wslc on a GitHub Actions windows-2025 runner.
- Can I run WSL containers on GitHub Actions?
- Yes, on the windows-2025 runner. It is Windows Server 2025 on an Azure VM size with hardware nested virtualisation, so vmcompute runs and the Hyper-V utility VM boots. Run `wsl --update --pre-release` first because the runner ships stable WSL without wslc, and call the binary by its absolute path C:\Program Files\WSL\wslc.exe since that directory is not on PATH. Do not use windows-11-arm: WSL does not work there because Azure Arm64 sizes have no nested virtualisation.
- Why does wslc say it is not recognised as a command?
- The wslc.exe binary is only laid down by pre-release WSL builds. Confirm with `wsl --version` that the WSL version is 2.9.3.0 or newer; if it is lower, the `--pre-release` flag did not take effect, usually because the Microsoft Store version of WSL was not the active one.
- Do I have to uninstall Docker Desktop first?
- No. wslc and Docker Desktop can coexist because wslc uses its own utility VM and its own image store. They do not share images, so anything you already pulled with Docker must be pulled again.
- How do I update wslc?
- Run <code>wsl --update --pre-release</code> for the latest pre-release build, or <code>wsl --update</code> to stay on your current channel. After updating, run <code>wsl --shutdown</code> to restart the subsystem. Check your version with <code>wsl --version</code> or <code>wslc version</code>.
Related guides
wslc vs Docker Desktop →
What you gain and what you give up by switching runtimes.
Migrating from Compose →
wslc has no Compose runtime — translate your stack field by field.
Command cheat sheet →
Every Docker command next to its wslc equivalent.
Command converter →
Paste a Docker command, get the wslc form instantly.