Windows Subsystem for Linux 2 (WSL2) transformed Docker on Windows from a sluggish, compatibility-limited experience into something approaching native Linux performance. Before WSL2, Docker on Windows relied on Hyper-V with a full Linux VM or Windows containers, both with significant overhead. WSL2 provides a real Linux kernel running inside Windows, and Docker takes full advantage of it.

This guide covers the complete setup from WSL2 installation through production-capable Docker development: Docker Desktop versus running Docker directly in WSL2, file system performance optimization, networking, VS Code integration, GPU passthrough for ML workloads, resource limits, and solutions to the most common problems.

WSL2 Installation

On Windows 10 (version 2004+) or Windows 11:

# Open PowerShell as Administrator

# Install WSL2 with Ubuntu (default)
wsl --install

# Or install a specific distribution
wsl --install -d Ubuntu-24.04

# List available distributions
wsl --list --online

# Verify WSL version
wsl --version

# Set WSL2 as default (if not already)
wsl --set-default-version 2

# Check installed distributions
wsl --list --verbose

After installation and reboot, set up your Linux user:

# Inside WSL2 Ubuntu
sudo apt update && sudo apt upgrade -y

# Install essential development tools
sudo apt install -y build-essential curl git wget unzip \
  ca-certificates gnupg lsb-release

Docker Desktop vs Docker in WSL2

There are two ways to run Docker on Windows with WSL2. Each has trade-offs:

Aspect Docker Desktop Docker Engine in WSL2
Installation Windows installer, GUI setup Manual Linux installation in WSL2
License Free for personal/small business, paid for enterprise (250+ employees) Free (open source Docker Engine)
GUI Dashboard, settings UI, extensions CLI only (or Portainer/usulnet)
Kubernetes Built-in single-node cluster Manual minikube/k3s installation
Windows integration System tray, PATH integration, Windows containers WSL2 terminal only
Resource usage ~1-2GB RAM overhead Lower overhead (no desktop process)
Updates Automatic via Docker Desktop Manual via apt

Option 1: Docker Desktop with WSL2 Backend

# 1. Download and install Docker Desktop from docker.com
# 2. In Docker Desktop Settings:
#    - General > "Use the WSL 2 based engine" (checked)
#    - Resources > WSL Integration > Enable for your distro

# 3. Verify from WSL2 terminal
docker version
docker compose version

Option 2: Docker Engine Directly in WSL2

# Inside WSL2 Ubuntu terminal

# Add Docker's official GPG key and repository
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) \
  signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

# Add your user to the docker group
sudo usermod -aG docker $USER

# Start Docker (WSL2 doesn't use systemd by default)
sudo service docker start

# Optional: Auto-start Docker when WSL2 opens
echo 'sudo service docker start' >> ~/.bashrc
Tip: WSL2 with Ubuntu 24.04+ supports systemd natively. Enable it by adding [boot]\nsystemd=true to /etc/wsl.conf inside your WSL2 distribution, then restart WSL (wsl --shutdown). With systemd enabled, Docker starts automatically and behaves identically to a standard Linux installation.

File System Performance

This is the single most important performance consideration for Docker on Windows with WSL2. File operations across the Windows-Linux boundary are dramatically slower than native operations.

File Location Speed from WSL2 Speed from Windows
WSL2 filesystem (~/projects) Native Linux speed Slow (9P protocol)
Windows filesystem (/mnt/c/Users/...) Very slow (9P protocol) Native Windows speed
Warning: Never store your project files on /mnt/c/ (the Windows filesystem) when using Docker in WSL2. File operations like npm install, go build, or git status will be 5-10x slower. Always clone repositories inside the WSL2 filesystem: ~/projects/myapp instead of /mnt/c/Users/you/projects/myapp.
# WRONG: Project on Windows filesystem (slow)
cd /mnt/c/Users/you/projects/myapp
docker compose up  # Bind mounts will be extremely slow

# CORRECT: Project on WSL2 filesystem (fast)
cd ~/projects/myapp
docker compose up  # Near-native Linux performance

# Access WSL2 files from Windows Explorer:
# \\wsl$\Ubuntu\home\you\projects\myapp

For Docker volumes, named volumes always perform well because they are stored within the WSL2 VM's virtual disk. Bind mounts perform well only when mounting from the WSL2 filesystem:

# Good: bind mount from WSL2 filesystem
volumes:
  - ./src:/app/src          # Fast (WSL2-native)
  - pgdata:/var/lib/postgresql/data  # Fast (named volume)

# Bad: bind mount from Windows filesystem
volumes:
  - /mnt/c/Users/you/src:/app/src  # Very slow

Networking

WSL2 networking has evolved through several modes. Understanding the current behavior is essential:

# Default (NAT mode): WSL2 gets its own IP address
hostname -I  # Shows WSL2's internal IP

# Ports exposed by Docker are accessible on localhost from Windows
# docker run -p 8080:80 nginx → accessible at http://localhost:8080 from Windows

# WSL2 can access Windows services via the host IP
cat /etc/resolv.conf  # nameserver shows the Windows host IP

Mirrored Networking Mode (Windows 11 22H2+)

# %USERPROFILE%/.wslconfig
[wsl2]
networkingMode=mirrored

# Benefits:
# - WSL2 shares Windows network interfaces
# - Same IP address as Windows
# - IPv6 support
# - VPN connectivity works automatically
# - Localhost forwarding is bidirectional

Note: If you use a corporate VPN, mirrored networking mode usually resolves the common issue of WSL2 losing internet connectivity when the VPN is active. This was historically one of the most frustrating WSL2 problems.

VS Code Integration

VS Code with the WSL extension provides the best development experience on Windows with Docker:

# From WSL2 terminal, open a project in VS Code
cd ~/projects/myapp
code .

# VS Code opens with the WSL extension, running the backend inside WSL2
# All file operations, terminal commands, and Docker operations use WSL2

Recommended VS Code extensions for Docker development on WSL2:

  • WSL (ms-vscode-remote.remote-wsl) - Required for WSL2 integration
  • Dev Containers (ms-vscode-remote.remote-containers) - Development inside Docker containers
  • Docker (ms-azuretools.vscode-docker) - Dockerfile and Compose support

The combination of WSL2 + VS Code + Dev Containers gives you a full Linux development environment with a native Windows IDE experience. Your code runs in Linux, your editor runs on Windows, and everything communicates seamlessly.

GPU Passthrough

WSL2 supports GPU passthrough for NVIDIA GPUs, enabling Docker containers to use GPU acceleration for machine learning and compute workloads:

# Prerequisites:
# 1. NVIDIA GPU driver installed on Windows (NOT in WSL2)
# 2. WSL2 with a supported distribution

# Verify GPU access in WSL2
nvidia-smi

# Install NVIDIA Container Toolkit in WSL2
distribution=$(. /etc/os-release; echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

# Test GPU in Docker
docker run --rm --gpus all nvidia/cuda:12.3.0-base-ubuntu22.04 nvidia-smi
# docker-compose.yml with GPU access
services:
  ml-training:
    image: pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    volumes:
      - ./models:/workspace/models
      - ./data:/workspace/data

Resource Limits

By default, WSL2 can consume up to 50% of system memory (or 8GB, whichever is less on older versions). For Docker workloads, you need to configure these limits explicitly:

# %USERPROFILE%/.wslconfig (Windows side)
[wsl2]
memory=8GB           # Limit WSL2 memory
processors=4         # Limit CPU cores
swap=4GB             # Swap file size
swapFile=C:\\temp\\wsl-swap.vhdx
localhostForwarding=true
networkingMode=mirrored

# Reclaim memory from WSL2 (useful for long-running sessions)
[experimental]
autoMemoryReclaim=gradual  # dropcache | gradual | disabled
sparseVhd=true             # Automatically shrink the virtual disk
# Apply changes (from PowerShell)
wsl --shutdown
wsl
Tip: Enable autoMemoryReclaim=gradual and sparseVhd=true in the [experimental] section. Without these, WSL2 progressively consumes memory and disk space that it never releases back to Windows, even after containers are stopped. These settings are stable despite being labeled "experimental."

Troubleshooting

Common Issues and Solutions

Problem Cause Solution
Docker commands hang Docker daemon not running sudo service docker start or restart Docker Desktop
Very slow file I/O Project on /mnt/c/ Move project to ~/projects/
No internet in WSL2 VPN or DNS issues Use mirrored networking or fix /etc/resolv.conf
Port already in use Windows service on same port Stop Windows service or use different port
WSL2 consuming all RAM No memory limit set Set memory in .wslconfig
Disk space growing Virtual disk does not shrink Enable sparseVhd=true or compact manually
Permission denied on bind mount UID mismatch Add user: "1000:1000" to Compose service
# Fix DNS issues (common with VPNs)
# In WSL2:
sudo rm /etc/resolv.conf
sudo bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
sudo bash -c 'echo "[network]\ngenerateResolvConf = false" > /etc/wsl.conf'

# Compact the WSL2 virtual disk (reclaim space)
# From PowerShell (WSL must be shut down):
wsl --shutdown
diskpart
# select vdisk file="C:\Users\you\AppData\Local\Packages\...\ext4.vhdx"
# attach vdisk readonly
# compact vdisk
# detach vdisk

Tips for Daily Use

  1. Always work from the WSL2 filesystem. Clone repos to ~/projects/, not /mnt/c/.
  2. Use Windows Terminal for the best WSL2 terminal experience. It supports tabs, profiles per distribution, and GPU-accelerated rendering.
  3. Set up SSH keys in WSL2, not on Windows. Git operations should run from WSL2.
  4. Use wsl --shutdown when done for the day. WSL2 does not release memory gracefully otherwise.
  5. Pin Docker Compose files to version 2 syntax (no version: key needed). Both Docker Desktop and Docker Engine support it.
  6. Use named volumes for databases. Never bind-mount database data directories across the Windows-Linux boundary.

For managing Docker containers and infrastructure running in WSL2, tools like usulnet provide a web-based interface that is accessible from your Windows browser, giving you visibility into container status, logs, and resource usage without switching between Windows and WSL2 terminals.