Fixing Docker Container “ERR_EMPTY_RESPONSE” — Stale WSL2 Backend & Port Shadowing

Docker Desktop (WSL2) on Windows 11

  • Software versions: Docker Desktop 4.19.0, WSL2 kernel 5.10.16.3, Windows 11 22H2 (OS Build 19045.2965)
  • Deploymentdocker-compose with Django + PostgreSQL + Redis + Mailpit + Celery workers
  • Critical paths:
    • Docker Compose file: ./compose/local/django/Dockerfile
    • Environment variables: ./.envs/.local/.django./.envs/.local/.postgres
    • Docker Diagnostic binary: C:\Program Files\Docker\Docker\resources\com.docker.diagnose.exe
    • WSL2 management: wsl --shutdown

Containers Report Healthy but Don’t Respond

We encountered this while performing routine updates on a development workstation. After a Windows reboot, both a local WordPress site and an Immich instance became unreachable in the browser. The first reflex was to run docker ps – all containers were Up and marked healthy.

We then attempted curl -v http://localhost:8000 from the host. The response was:

* Empty reply from server
* Closing connection 0
curl: (52) Empty reply from server

Inside the container (docker exec -it <container> sh), a curl against 127.0.0.1:8000 returned the expected application HTML. That told us the application was running fine, but the host‑to‑container network path was broken.

We also noticed that the same docker-compose stack worked flawlessly on a colleague’s Windows laptop, so the issue was not in the code or the image, but in our local Docker environment.


VPNKit Missing and Filesystem Corruption

To gather more evidence, we ran the Docker Desktop diagnostic tool. The output showed multiple failures:

[FAIL] DD0029: is the WSL 2 Linux filesystem corrupt?
[ 15.727809] EXT4-fs error (device sdd): ext4_put_super:1188: comm Xwayland: Couldn't clean up the journal
[FAIL] DD0014: are the backend processes running?
1 error occurred: * com.docker.vpnkit.exe is not running
[FAIL] DD0009: is the vpnkit API responding?
open \\.\pipe\dockerVpnKitDiagnostics: The system cannot find the file specified.

The full check output (excerpted above) confirmed that VPNKit was dead and the WSL2 filesystem was flagged as corrupt. This matched the behaviour we observed – docker ps still worked because the Docker engine itself was alive, but the networking component that proxies ports between Windows and WSL2 had failed.


What We Tried (and Why It Didn’t Work)

We initially tried the obvious fixes, which all proved fruitless:

ActionOutcome
Restarting the containers individually (docker restart)No change – still empty replies
Rebinding ports in docker-compose.yml (e.g., 8000:8000 → 8001:8000)New containers started without port‑in‑use errors, but browsers still got no data
Clearing browser cache, using incognito modeRuled out client‑side issues
Disabling Windows firewall and antivirusNo effect
Re‑running migrations and resetting the databaseApplication logs showed successful startup, but networking remained broken
Adding healthcheck delays and depends_on conditionsEnsured services were ready, but didn’t touch the networking layer

The key clue was that new containers could bind to the same ports without any conflict error. This suggested stale processes were still holding those ports at the kernel level, invisible to docker ps – a port‑shadowing scenario.


Orphaned Processes from a Corrupted WSL2 Filesystem

The diagnostic errors pointed directly to the root cause:

  1. WSL2’s ext4 filesystem had become corrupted (likely due to an unclean shutdown after a Windows update). The kernel journal could not be cleaned, leaving the VM in an inconsistent state.
  2. When Docker Desktop attempted to restart containers, some processes became orphaned – they were still running inside the WSL2 VM (ps aux inside the VM showed them), but the Docker daemon’s internal state lost track of them.
  3. These orphaned processes kept their TCP ports open. Because VPNKit was also down, the Docker networking layer did not enforce exclusive port binding, so new containers appeared to start normally but never actually received any incoming traffic.
  4. Without VPNKit, the port‑forwarding chain from Windows (localhost:8000) to the container’s internal IP was completely severed – hence the ERR_EMPTY_RESPONSE.

We confirmed this by running netstat -ano | findstr :8000 on Windows, which showed a PID that didn’t match any Docker container ID. Cross‑referencing with wsl -d docker-desktop ps aux revealed that PID belonged to a stale Python process from an earlier run.


Resetting the WSL2 VM and Cleaning Orphans

The correction targets the corrupted VM state, not the containers themselves.

Step 1 – Force‑shut down WSL2

In an elevated PowerShell window:

wsl --shutdown

This terminates the VM. On the next start, WSL2 will automatically run fsck on the ext4 volume, which often repairs the journal corruption.

Step 2 – Restart Docker Desktop

  • Exit Docker Desktop completely (right‑click tray icon → Quit Docker Desktop).
  • Launch it again.

After the restart, we re‑ran the diagnostic check:

& "C:\Program Files\Docker\Docker\resources\com.docker.diagnose.exe" check

All failures (DD0029DD0014DD0009) were now passing.

Step 3 – Remove orphaned container filesystems

Even though docker ps -a showed no containers (after the WSL shutdown), some residual container data might persist. To be safe, we removed everything:

docker rm -f $(docker ps -aq) 2>/dev/null

(We used 2>/dev/null to ignore warnings about no containers.)

Step 4 – Recreate the stack

Finally, we brought everything back up:

docker compose up -d --force-recreate

The --force-recreate ensures fresh containers are built, free from any hidden state.


Confirming the Fix

After the restart, we ran the following checks:

  • docker ps – all containers showed Up.
  • curl -v http://localhost:8000 – returned the Django welcome page with HTTP 200.
  • Browser navigation to http://localhost:8000 loaded the application normally.
  • The diagnostic tool returned all [PASS] entries.

We also monitored the memory and CPU usage for a few minutes (docker stats) – no abnormal spikes, and the containers remained responsive under a light load test.


Monitoring and Maintenance Habits

To avoid a recurrence, we’ve adopted these practices:

  • Weekly WSL2 shutdown – run wsl --shutdown after long uptime or before suspending the host.
  • Keep Docker Desktop and Windows updated – the filesystem corruption is known to be fixed in newer Insider builds; production hosts should run at least Windows 11 23H2 with the latest cumulative updates.
  • Automated diagnostic checks – include com.docker.diagnose.exe check in a weekly scheduled task; pay special attention to DD0029 and DD0009.
  • Port‑conflict awareness – if you see containers starting without port‑in‑use errors, immediately check for orphaned processes using netstat and wsl ps aux.
  • Container pruning – a monthly docker system prune -f helps keep the environment clean.

References


(Last verified: 3 July 2026 — Docker Desktop v4.19.0, Windows 11 22H2 / WSL2)