DEV Community

Cover image for 🐳 🔄 Docker Desktop + WSL2 on Windows 11
João Victor
João Victor

Posted on

🐳 🔄 Docker Desktop + WSL2 on Windows 11

I currently maintain two development environments: one Linux and one Windows 11.

On Linux, this kind of problem is much less frequent for me. On Windows 11, however, Docker Desktop + WSL2 occasionally ends up in a strange state: Docker gets stuck on Loading, docker-desktop shows up as Installing, the daemon doesn't respond, or WSL is left with stuck processes/services.

After going through this a few times, I ended up keeping a saved PowerShell script to perform a more aggressive reset of the environment.

The idea is simple: restart what needs to be restarted without wiping my WSL distributions or my development environment.

The script

Run PowerShell as Administrator.

#requires -RunAsAdministrator

$ErrorActionPreference = "SilentlyContinue"

Write-Host "=== Docker Desktop + WSL2 - Hard reset ===" -ForegroundColor Cyan
Write-Host ""
#
# Terminates the WSL client to release processes that may be stuck.
taskkill.exe /F /IM "wsl.exe" 2>$null

# Terminates the WSL distribution hosts that may remain stuck.
taskkill.exe /F /IM "wslhost.exe" 2>$null

# Terminates the in-process WSL service in case it is stuck during shutdown.
taskkill.exe /F /IM "wslservice.exe" 2>$null

# Terminates the VMM process used by the WSL2 distributions' backend.
taskkill.exe /F /IM "VmmemWSL.exe" 2>$null

# Completely closes the Docker Desktop interface.
taskkill.exe /F /IM "Docker Desktop.exe" 2>$null

# Terminates the main Docker Desktop backend.
taskkill.exe /F /IM "com.docker.backend.exe" 2>$null

# Terminates the HNS process when it gets stuck in STOP_PENDING or START_PENDING.
taskkill.exe /F /IM "svchost.exe" /FI "SERVICES eq hns"

Write-Host ""
Write-Host "[1/8] Stopping services..." -ForegroundColor Yellow

# Tries to stop the modern WSL service if available in this installation.
Stop-Service WSLService -Force -ErrorAction SilentlyContinue

# Tries to stop the legacy service if it exists in older WSL versions.
Stop-Service LxssManager -Force -ErrorAction SilentlyContinue

# Stops the compute service used by virtual machines and WSL2.
Stop-Service vmcompute -Force -ErrorAction SilentlyContinue

# Stops the Host Network Service used by WSL2 and Docker networking.
Stop-Service hns -Force -ErrorAction SilentlyContinue

Write-Host "[2/8] Checking HNS..." -ForegroundColor Yellow

# Displays the detailed HNS state to identify stuck processes.
sc.exe queryex hns

Write-Host ""
Write-Host "[3/8] Checking VM Compute..." -ForegroundColor Yellow

# Displays the detailed state of the virtual machine compute service.
sc.exe queryex vmcompute

Write-Host ""
Write-Host "[4/8] Starting services again..." -ForegroundColor Yellow

# Starts the compute service required by the WSL2 backend.
Start-Service vmcompute -ErrorAction SilentlyContinue

# Starts the Host Network Service used by the Docker and WSL2 virtual network.
Start-Service hns -ErrorAction SilentlyContinue

# Starts the modern WSL service when available in this version.
Start-Service WSLService -ErrorAction SilentlyContinue

Write-Host ""
Write-Host "[5/8] Service status..." -ForegroundColor Yellow

# Shows only the WSL, HNS and VM Compute services available on this machine.
Get-Service WSLService,LxssManager,hns,vmcompute -ErrorAction SilentlyContinue |
    Format-Table Status,Name,DisplayName -AutoSize

Write-Host ""
Write-Host "[6/8] WSL status..." -ForegroundColor Yellow

# Lists all distributions and confirms whether the Docker backend is registered.
wsl.exe -l -v

# Displays the current configuration of the WSL2 installed on the machine.
wsl.exe --status

Write-Host ""
Write-Host "[7/8] Starting Docker Desktop..." -ForegroundColor Yellow

# Starts the Docker Desktop service before opening its interface.
Start-Service com.docker.service -ErrorAction SilentlyContinue

# Opens Docker Desktop to reinitialize the Linux backend.
$dockerDesktop = "$env:ProgramFiles\Docker\Docker\Docker Desktop.exe"

if (Test-Path $dockerDesktop) {
    Start-Process $dockerDesktop
}
else {
    Write-Warning "Docker Desktop.exe not found at: $dockerDesktop"
}

Write-Host ""
Write-Host "[8/8] Waiting for the Docker backend..." -ForegroundColor Yellow

# Waits for Docker Desktop to start its distribution and its internal daemon.
Start-Sleep -Seconds 15

# Shows the state of the WSL distributions again after startup.
wsl.exe -l -v

Write-Host ""
Write-Host "=== Docker validation ===" -ForegroundColor Cyan

# Tests whether the Docker client can connect to the Linux daemon.
docker.exe version

# Shows daemon information to confirm the backend is operational.
docker.exe info

# Displays the Docker contexts and highlights the currently selected one.
docker.exe context ls

Write-Host ""
Write-Host "=== Reset complete ===" -ForegroundColor Green
Enter fullscreen mode Exit fullscreen mode

What does it do?

Basically, the script tries to clear stuck WSL2, Docker Desktop and HNS processes, restarts the necessary services, and then validates the state of WSL and Docker.

The most important point, in my view, is that it does not remove my Ubuntu distributions and does not start by deleting containers, volumes or images.

A real example

In one of the cases I ran into, the environment was inconsistent.

WSL showed something like:

NAME              STATE        VERSION
Ubuntu-22.04      Stopped      2
docker-desktop    Installing   2
Ubuntu            Stopped      2
Enter fullscreen mode Exit fullscreen mode

While Docker returned a connection error with:

dockerDesktopLinuxEngine
Enter fullscreen mode Exit fullscreen mode

And an attempt to directly access:

wsl.exe -d docker-desktop
Enter fullscreen mode Exit fullscreen mode

returned:

Wsl/Service/WSL_E_DISTRO_NOT_FOUND
Enter fullscreen mode Exit fullscreen mode

I've also found HNS stuck in states such as:

START_PENDING
Enter fullscreen mode Exit fullscreen mode

or:

STOP_PENDING
Enter fullscreen mode Exit fullscreen mode

In these cases, simply running wsl --shutdown or restarting Docker Desktop was not enough.

An important detail

The script is a recovery routine, not a guarantee that Docker Desktop will start automatically.

For example, in one run Windows managed to leave:

hns        Running
vmcompute  Running
WSLService Running
Enter fullscreen mode Exit fullscreen mode

and WSL went back to showing:

docker-desktop    Stopped    2
Enter fullscreen mode Exit fullscreen mode

but the Docker Desktop executable was not at the path the script expected.

In that scenario, the script still did its part in recovering WSL/services, but Docker Desktop had to be started manually.

This is also useful because it shows that "healthy WSL" and "healthy Docker daemon" are different things.

⚠️ Be careful with wsl --unregister

There is a second procedure that I keep separate from the main script:

wsl.exe --unregister docker-desktop
Enter fullscreen mode Exit fullscreen mode

This command is not a restart.

wsl --unregister removes the specified WSL distribution. Therefore, it can remove data stored in that distribution and, depending on how Docker Desktop is configured, affect Docker's internal state.

For that reason, I do not put this command in the normal script flow.

I only use it in a specific situation where:

docker-desktop    Installing
Enter fullscreen mode Exit fullscreen mode

but:

wsl.exe -d docker-desktop
Enter fullscreen mode Exit fullscreen mode

returns:

Wsl/Service/WSL_E_DISTRO_NOT_FOUND
Enter fullscreen mode Exit fullscreen mode

Even in that case, it should be treated as a last resort.

And a personal rule in my environment:

DO NOT run:

wsl --unregister Ubuntu
wsl --unregister Ubuntu-22.04
Enter fullscreen mode Exit fullscreen mode

These are my development distributions and are not part of the normal Docker recovery process.

My summary

For me, this script has become a kind of "hard reset" for Docker Desktop + WSL2 on Windows 11.

It is not an official Docker or Microsoft procedure. It is simply a personal recovery playbook based on the problems I've run into in my development environment.

Having the script saved is much more practical than manually repeating the same sequence every time Docker Desktop decides to get stuck on Loading.

Top comments (0)