Using WSL as a Docker Desktop Alternative¶
Wsl Manager enables you to create a lightweight Arch Linux instance that serves as a complete replacement for Docker Desktop on Windows. This approach provides several advantages:
- Lighter resource usage - No heavy virtualization overhead
- Faster startup times - WSL instances boot much quicker than full VMs
- Native Linux environment - Direct access to Linux tooling and packages
- Cost-effective - Completely free alternative to Docker Desktop licensing
- Full control - Customize your container environment exactly as needed
This guide demonstrates how to set up an Arch Linux WSL instance specifically configured for container development, giving you all the functionality of Docker Desktop with improved performance and flexibility.
Installation¶
First install the distribution:
PS> New-WslInstance docker -From arch
⌛ Creating directory [C:\Users\AntoineMartin\AppData\Local\Wsl\docker]...
⌛ Creating instance [docker] from [C:\Users\AntoineMartin\AppData\Local\Wsl\RootFS\docker.arch.rootfs.tar.gz]...
🎉 Done. Command to enter instance: Invoke-WslInstance -In docker or wsl -d docker
Name State Version Default
---- ----- ------- -------
docker Stopped 2 False
PS>
Connect to it as root and install docker:
PS> # Add docker to the distribution
PS> iwsl -In docker -User root pacman -Sy --noconfirm --needed docker
:: Synchronizing package databases...
core is up to date
extra 7.8 MiB 42.3 MiB/s 00:00 [########################################] 100%
...(omitted for brevity)...
(4/4) Arming ConditionNeedsUpdate...
PS>
Add the arch
user to the docker group:
Use WSL docker client from Windows¶
With this method, When you type docker
in Windows, it actually executes the
docker client in the docker
WSL instance.
In order to do that, you need to define an alias on Windows. This is done by
adding the following to
$Env:USERPROFILE\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1.ps1
($PROFILE
variable):
docker_profile.ps1
The Add-DockerProfile.ps1
script adds the contents of the
docker_profile.ps1
file to your Powershell profile.
Import-Module Wsl-Manager
function Get-WslDockerInstance {
[CmdletBinding()]
[OutputType([WslInstance])]
param(
[Parameter(Mandatory = $false)]
[Alias("Name")]
[string]$InstanceName = $Env:DOCKER_WSL
)
if (-not $InstanceName) {
$InstanceName = "docker"
}
return Get-WslInstance -Name $InstanceName
}
function Start-WslDocker {
[CmdletBinding(SupportsShouldProcess=$true)]
[OutputType([WslInstance])]
param(
[Parameter(Mandatory = $false, ParameterSetName = "Name")]
[Alias("Name")]
[string]$InstanceName = $Env:DOCKER_WSL,
[Parameter(Mandatory = $true, ParameterSetName = "Instance", ValueFromPipeline = $true)]
[WslInstance]$Instance
)
process {
if ($PSCmdlet.ParameterSetName -eq "Name") {
$Instance = Get-WslDockerInstance -InstanceName $InstanceName
}
if ($PSCmdlet.ShouldProcess($Instance.Name, "Starting Docker in WSL instance")) {
Invoke-WslInstance -Instance $Instance -User root /bin/sh "-c" "test -f /var/run/docker.pid || sudo -b sh -c 'dockerd -p /var/run/docker.pid -H unix:// -H tcp://0.0.0.0:2376 >/var/log/docker.log 2>&1'" | Out-Null
}
$Instance
}
}
function Stop-WslDocker {
[CmdletBinding(SupportsShouldProcess=$true)]
[OutputType([WslInstance])]
param(
[Parameter(Mandatory = $false, ParameterSetName = "Name")]
[Alias("Name")]
[string]$InstanceName = $Env:DOCKER_WSL,
[Parameter(Mandatory = $true, ParameterSetName = "Instance", ValueFromPipeline = $true)]
[WslInstance]$Instance
)
process {
if ($PSCmdlet.ParameterSetName -eq "Name") {
$Instance = Get-WslDockerInstance -InstanceName $InstanceName
}
if ($PSCmdlet.ShouldProcess($Instance.Name, "Stopping Docker in WSL instance")) {
Invoke-WslInstance -Instance $Instance -User root /bin/sh "-c" 'test -f /var/run/docker.pid && sudo kill `cat /var/run/docker.pid`' | Out-Null
}
$Instance
}
}
function Invoke-WslDocker {
[CmdletBinding()]
param(
[Parameter(Mandatory = $false, ValueFromRemainingArguments = $true)]
[string[]]$Arguments
)
process {
Start-WslDocker -Name $Env:DOCKER_WSL | Invoke-WslInstance -User root docker @Arguments
}
}
Set-Alias -Name docker -Value Invoke-WslDocker
$ContentToAdd = try {
(Invoke-WebRequest -Uri "https://raw.githubusercontent.com/antoinemartin/PowerShell-Wsl-Manager/main/docs/examples/docker_profile.ps1" -UseBasicParsing).Content
} catch {
Get-Content $PSScriptRoot\docker_profile.ps1 -Raw
}
$StartComment = "### START Adding Docker alias"
$EndComment = "### END Adding Docker alias"
# Get $PROFILE, Remove existing Docker alias and add new one
$NewBlock = "`n$StartComment`n$ContentToAdd`n$EndComment`n"
if (Test-Path -Path $PROFILE) {
$ProfileContent = Get-Content -Path $PROFILE -Raw
# Remove existing block
$Pattern = [regex]::Escape($StartComment) + '.*?' + [regex]::Escape($EndComment)
$ProfileContent = [regex]::Replace($ProfileContent, $Pattern, '', [System.Text.RegularExpressions.RegexOptions]::Singleline)
# Append new block
$UpdatedContent = $ProfileContent + $NewBlock
Set-Content -Path $PROFILE -Value $UpdatedContent
} else {
# Create new profile with the Docker alias block
New-Item -Path $PROFILE -ItemType File -Force
Set-Content -Path $PROFILE -Value $NewBlock
}
Once you open a new powershell window, you can run docker and run docker directly from powershell:
PS> docker run --rm -it alpine:latest /bin/sh
Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
c158987b0551: Pull complete
Digest: sha256:8914eb54f968791faf6a8638949e480fef81e697984fba772b3976835194c6d4
Status: Downloaded newer image for alpine:latest
/ # exit
PS>
The docker
alias starts docker automatically if it's not already running.
You can stop docker with:
You can save the instance as an image for reuse:
And then create another distribution in the same state from the exported root filesystem:
Only one WSL instance can be active at a time
Only one docker daemon can be active at a time. This is because docker
creates virtual network interfaces, docker0
in particular.
As the network interface namespace is shared between WSL instances, this
creates collisions.
You can then flip between the two distributions:
# Run nginx in docker distribution
❯ docker run '-d' '-p' 8080:80 '--name' nginx nginx:latest
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
a603fa5e3b41: Pull complete
c39e1cda007e: Pull complete
90cfefba34d7: Pull complete
a38226fb7aba: Pull complete
62583498bae6: Pull complete
9802a2cfdb8d: Pull complete
Digest: sha256:e209ac2f37c70c1e0e9873a5f7231e91dcd83fdf1178d8ed36c2ec09974210ba
Status: Downloaded newer image for nginx:latest
61f5993c6e1ad87a35f1d6dacef917b5f6d0951bdd3e5c31840870bdac028f91
# View it running
PS> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
61f5993c6e1a nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp nginx
PS> # Switch to other instance
PS> Stop-WslDocker; $env:DOCKER_WSL="docker2"; Start-WslDocker
# Clean docker instance !
PS> docker ps '-a'
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Use Windows docker client on Windows¶
While the above solution works well, you may also want to use the Windows docker client directly from Windows. This can be done by configuring the Windows docker client to communicate with the WSL docker daemon.
To do this, you need to set the DOCKER_HOST
environment variable in your
Windows PowerShell profile to point to the WSL docker daemon. You can add the
following line to your PowerShell profile:
After adding this line, you can use the Windows docker client as you normally would, and it will communicate with the WSL docker daemon.
If you are using scoop, you can add the docker client to Windows with:
if you have installed the docker alias in your profile, you need to remove it:
And then you can use the Windows docker client as you normally would:
PS> # Quoting arguments is not needed anymore
PS> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66ada31ac41b nginx:latest "/docker-entrypoint.…" 6 minutes ago Exited (0) 6 minutes ago nginx
PS> docker restart nginx
nginx
PS> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66ada31ac41b nginx:latest "/docker-entrypoint.…" 7 minutes ago Up 7 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp nginx
PS>
To remove the alias from your profile, you can use the following command: