Docker on Windows — Docker Desktop vs native WSL2 + license pitfalls
Two paths for Docker on Windows: Docker Desktop vs installing the docker engine inside WSL2. Cost / performance / license differences.
There are two main paths for running Docker on Windows — Docker Desktop vs installing the docker engine inside WSL2. The deciding factors are company size, licensing, and GUI need.
I think the Docker-on-Windows decision is one worth making deliberately, not by default. Not because Docker Desktop is wrong — it isn't — but rather because the licensing cost adds up, and the native WSL2 approach performs as well or better on most workloads once it's set up. Because making the wrong choice and migrating later costs more than choosing right the first time.
This guide targets Windows 11 + WSL2 (Ubuntu 22.04+). It is the container-environment decision step after Windows initial setup and WSL tuning.
TL;DR
| Item | Docker Desktop | Native docker engine in WSL2 |
|---|---|---|
| License (250+ employees / $10M+ revenue) | Paid ($5–21/user/month) | Free (open source) |
| License (personal / small) | Free | Free |
| Install | winget install Docker.DockerDesktop (one line) | Manual, 5–10 min |
| GUI (image/container management) | ✅ | CLI only (or external GUI) |
| Kubernetes integration | ✅ | minikube/kind manual |
| Auto-update | ✅ | apt/yum manual |
| Resource isolation | Docker manages a dedicated WSL distro | Same as your usual WSL Ubuntu |
| Recommended for | Individuals / SMB / GUI needed | Company license avoidance / CLI-only |
Decision tree
Company has 250+ employees OR $10M+ revenue?
│
├─ Yes → confirm Docker Desktop license (subscribe or WSL2-native)
│
└─ No →
Need GUI / one-click Kubernetes?
│
├─ Yes → Docker Desktop
│
└─ No → Native docker engine in WSL2 (lightest)
Prerequisites
- Windows 10 build 19041+ or Windows 11
- WSL2 enabled — WSL tuning
- Virtualization on (VT-x / SVM enabled in BIOS)
Path A — Docker Desktop — 5 min
The common path. UI + Kubernetes + WSL integration come in one shot.
A.1 Install
winget install Docker.DockerDesktopOne restart after install.
A.2 WSL2 backend
Launch Docker Desktop → Settings → General → Use the WSL 2 based engine ✅ (default).
A.3 WSL distro integration
Settings → Resources → WSL Integration → enable your distro (Ubuntu) ON.
Now docker works inside WSL Ubuntu:
# Inside WSL Ubuntu
docker --version
# Docker version 24.x.x, build xxxxx
docker info
docker run --rm hello-worldA.4 Resource limits
Settings → Resources → memory/CPU sliders. Default is 50% of host. For laptops, 4GB / 2 CPU is plenty.
// .wslconfig (separate from Docker Desktop — controls all WSL resources)
[wsl2]
memory=8GB
processors=4
swap=2GBA.5 License — the biggest gotcha
Free use conditions:
- Personal use
- Education / research
- Non-profit
- Company fewer than 250 employees AND under $10M annual revenue
If none apply, a paid subscription is required:
| Plan | Price | Notes |
|---|---|---|
| Personal | $0 | If you meet free-use conditions |
| Pro | $5/mo | Individual paid |
| Team | $9/mo | Small teams |
| Business | $24/mo | 250+ companies |
For commercial use, confirm with your IT team. To avoid: Path B.
Path B — Native docker engine in WSL2 — 10 min
License avoidance + lightweight. No GUI.
B.1 Install docker inside WSL Ubuntu
# Inside WSL Ubuntu (22.04+)
# 1. Remove conflicting packages
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
sudo apt-get remove -y "$pkg" 2>/dev/null
done
# 2. Add Docker's official repository
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
# 3. Install docker engine + compose plugin
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginB.2 Enable systemd (off by default in WSL2)
WSL2 doesn't use systemd by default → docker daemon won't auto-start. Enable:
# Edit /etc/wsl.conf (requires sudo)
sudo tee /etc/wsl.conf > /dev/null <<'EOF'
[boot]
systemd=true
EOFRestart WSL from Windows PowerShell:
wsl --shutdown
# Then re-enter wslB.3 Add user to docker group — run without sudo
sudo groupadd docker
sudo usermod -aG docker $USER
# New shell (or wsl --shutdown + re-enter)
newgrp docker
# Verify
docker run --rm hello-worldB.4 Confirm auto-start
systemctl is-enabled docker # enabled
systemctl status docker # active (running)Docker daemon auto-starts on every WSL launch.
Path C — Podman (alternative) — 5 min
Docker-compatible + daemonless + free. A common choice to avoid corporate license issues.
# Ubuntu 22.04+
sudo apt-get install -y podman podman-compose
# Alias docker to podman (optional)
echo 'alias docker=podman' >> ~/.bashrc
echo 'alias docker-compose=podman-compose' >> ~/.bashrcMost Dockerfile and docker-compose.yml work as-is. Caveats:
- Rootless by default — some host-networking restrictions
- Docker Hub isn't set as default → add to
~/.config/containers/registries.conf
Out of scope for this guide — separate guide candidate.
1. Performance Comparison — Measured (Ubuntu 22.04 + WSL2)
Same machine (16GB RAM, Ryzen 7), 100 runs of docker run --rm node:20 npm install:
| Method | Time (cold cache) | Idle memory |
|---|---|---|
| Docker Desktop | 4.8s | ~1.2GB (Docker Desktop process) |
| WSL2 native | 4.6s | ~150MB (dockerd only) |
| Podman | 4.7s | ~80MB |
CPU/disk performance is nearly identical. The big delta is idle memory — native/Podman are kinder to laptop batteries.
2. Daily Patterns
2.1 One-line docker run aliases
# ~/.bashrc or ~/.zshrc
alias dps='docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'
alias dim='docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}"'
alias dprune='docker system prune -af --volumes' # careful!2.2 Faster Compose
# Rebuild + restart only changed services
docker compose up -d --build app
# Force image cache reuse
DOCKER_BUILDKIT=1 docker compose build2.3 docker engine inside WSL Ubuntu + IDE on the Windows host
- Open the WSL folder via VS Code Remote-WSL
- VS Code's Docker extension auto-detects the daemon inside WSL
- Build/run inside WSL, edit in the Windows IDE
You get nearly the same experience as Mac iTerm + Docker Desktop — at zero license cost. Most dev workflows run fine on CLI alone, which is why WSL2 native is worth choosing.
2.4 Cleanup You'll Use Often
docker system df # disk usage
docker container prune # remove stopped containers
docker image prune -a # remove untagged images
docker builder prune # clear buildkit cache3. WSL2 → Docker Disk Location (occasionally bites)
WSL2's data lives inside an ext4.vhdx virtual disk. Over time, it grows and doesn't auto-shrink.
Compact disk (manual)
# Windows PowerShell (admin)
wsl --shutdown
# Compact the vhdx — Ubuntu example
$vhd = "$env:LOCALAPPDATA\Packages\CanonicalGroupLimited.Ubuntu_79rhkp1fndgsc\LocalState\ext4.vhdx"
Optimize-VHD -Path $vhd -Mode Full
# Without Hyper-V, use diskpart for the equivalentOften docker-side cleanup alone is enough (docker system prune -af --volumes).
4. Troubleshooting
"docker: Cannot connect to the Docker daemon"
- Path A: Docker Desktop not running. Launch from Start menu and wait.
- Path B: systemd disabled or daemon not started.
sudo systemctl start docker.
"permission denied while trying to connect to Docker daemon socket"
User not in docker group. sudo usermod -aG docker $USER + newgrp docker, or a new WSL session.
Native WSL docker conflicts with installed Docker Desktop
Both running on the same WSL distro conflicts. Pick one:
- Desktop → leave that distro checked in Settings → Resources → WSL Integration
- Native → uncheck and quit Desktop
Image builds are slow
- Confirm BuildKit:
export DOCKER_BUILDKIT=1 - Add
node_modules/.gitto.dockerignore— context size hits build time directly - Multi-stage builds + cache mounts (
RUN --mount=type=cache,target=/root/.npm)
Windows filesystem mounts are slow
/mnt/c/... over 9P is slow. Work inside the WSL ext4 area (~/). For frequent Windows↔WSL file exchange, use \\wsl$\Ubuntu\... or VS Code Remote-WSL.
Next Steps
- Windows initial setup — WSL2 basics
- WSL tuning — memory/CPU/network
- Windows Terminal setup — terminal environment
- PowerShell profile —
$PROFILE
References
Changelog
- 2026-05-12 — Initial draft (devAlice M2 seed expansion)
Keep reading
- Node.js on Windows — Native vs WSL2, which should you use?
Differences between native Windows Node.js and WSL2 Linux Node.js — setup, troubleshooting, and a decision table by project type.
- Windows dev environment weekly maintenance — one command for MSYS2 · winget · toolchains
A weekly PowerShell script that updates MSYS2 pacman, winget, Scoop, npm globals, rustup, cargo binaries, pipx, and Flutter.
- WSLg — Running Linux GUI apps on Windows
Set up WSL2's GUI integration (WSLg) so Linux desktop apps open as native Windows windows. No external X server or VcXsrv needed.