WSL2 Setup That Doesn’t Break Docker (Yes, There’s a Right Way)

Was this helpful?

Most “WSL2 + Docker” guides are written like nobody has ever shipped software under deadline pressure. They skip the parts where your containers crawl because you cloned a repo into the wrong directory, or Docker Desktop decides your images are “gone” because you “cleaned up” WSL. Then Monday happens.

This is the production-minded way to set up WSL2 so Docker stays fast, predictable, and recoverable. It’s opinionated because reality is opinionated.

The right way: what you’re actually building

You are not “installing Linux on Windows.” You are building a small, managed virtualization stack:

  • Windows hosts the kernel-adjacent infrastructure (Hyper-V components) that WSL2 uses.
  • WSL2 runs one or more Linux distributions inside a lightweight VM. Each distro has its own virtual disk (a VHDX).
  • Docker runs either:
    • Docker Desktop, which runs its own Linux VM and can integrate with WSL2 distros, or
    • Docker Engine inside a WSL2 distro (no Docker Desktop), with systemd and Linux-native behavior.

The “doesn’t break” setup comes down to three themes:

  1. Put the right files on the right filesystem. Linux-on-Linux for builds, node_modules, and image layers. Windows paths only when you truly need Windows tooling.
  2. Control resources deliberately. WSL2 will happily eat RAM and disk until your laptop becomes a space heater with a keyboard.
  3. Pick one Docker ownership model. Either Docker Desktop owns Docker, or your Linux distro does. Mixing them without intent is how you get “it worked yesterday.”

One quote worth keeping above your monitor: Hope is not a strategy. — General Gordon R. Sullivan

Interesting facts and context (so you stop making 2019 mistakes)

  • WSL1 wasn’t a VM. It translated Linux syscalls into Windows kernel calls. Great trick, but it wasn’t “real Linux,” and Docker-in-WSL1 was a science project.
  • WSL2 switched to a real Linux kernel. That’s why Docker became practical. It’s also why you now have a real virtual disk that can fill up and fragment.
  • Early WSL2 builds had notorious file I/O pain on /mnt/c. This is why “clone into Linux home” became the default advice—and it’s still correct for most dev workloads.
  • Docker Desktop moved its backend multiple times. Hyper-V backend, then WSL2 backend, plus shifting integration behavior across versions. Old blog posts lie by accident.
  • WSL2 networking uses NAT. That’s why the WSL2 VM has its own IP and why some VPN/DNS setups behave like they’re haunted.
  • VHDX “shrink” isn’t automatic. Deleting files in Linux doesn’t necessarily return space to Windows. You need explicit compaction steps.
  • Systemd support in WSL is recent. For years it was “no systemd,” which changed how services and Docker behaved. Now you can enable it, but many guides still assume you can’t.
  • Case sensitivity differences are real. Windows filesystems are usually case-insensitive; Linux is case-sensitive. This shows up as weird Git diffs, broken builds, or duplicate files.

WSL2 + Docker: architecture and the sharp edges

Two supported models (pick one)

Model A: Docker Desktop + WSL2 integration. Docker Desktop owns the daemon. Your WSL distro gets Docker CLI access and can run containers, but the “real” Docker state lives in Docker Desktop’s managed environment.

Model B: Docker Engine inside a WSL2 distro. Your distro owns the daemon and storage. This feels more like a Linux server. It’s great if you want Linux-native behavior and fewer surprises from a GUI app.

The storage reality: where performance lives or dies

WSL2 Linux filesystem (your distro’s ext4 inside VHDX) is fast for Linux tooling. Windows-mounted files under /mnt/c are slower and have metadata translation overhead. Docker bind mounts and build contexts amplify that overhead.

If you run builds on Windows paths and mount them into Linux containers, you are asking for performance problems. You may also be asking for file watcher weirdness (especially with Node, Python, or any tool that thinks in inotify).

Networking: “localhost works” until it doesn’t

WSL2 has its own virtual NIC. Windows and WSL2 cooperate so that “localhost” often works both ways, but the path is not symmetric and not guaranteed under VPNs, firewalls, or custom DNS.

Docker adds its own network namespaces and bridge networks inside the Linux environment. Translation layers stack, and every translation layer is a new place for latency, MTU issues, or name resolution to go wrong.

Joke #1: NAT is like middle management—useful in theory, but sometimes your packets leave the meeting more confused than when they entered.

Non-negotiable rules that keep Docker from breaking

Rule 1: Keep source code in the Linux filesystem unless you have a strong reason

Default: clone into ~/src inside WSL. Build there. Run Docker from there. Your life improves.

Exceptions exist (Windows-only IDE workflows, corporate endpoint scanning constraints), but you should treat exceptions like production change requests: documented, justified, reversible.

Rule 2: Don’t let Docker data drift across ownership boundaries

If you use Docker Desktop, let it own the engine and its storage. Don’t also run a separate dockerd inside your distro unless you’re intentionally running two daemons (most people aren’t).

Rule 3: Control WSL resources with a .wslconfig

Unbounded WSL can starve Windows. That can look like “Docker is slow,” “VS Code is laggy,” or “my fan is now my primary input device.” Put limits in place, then tune.

Rule 4: Treat WSL distros like cattle, but export before surgery

WSL makes it easy to nuke and recreate a distro. That’s a feature. But Docker images, volumes, and databases inside WSL are state. Export or snapshot before you “just reinstall Ubuntu real quick.”

Rule 5: Never debug performance without measuring the filesystem boundary

When Docker feels slow, you need to answer: are we doing slow I/O on /mnt/c, or is this CPU/memory/network? Guessing is how you burn afternoons.

Practical tasks (commands, outputs, decisions)

These are the day-to-day commands that keep WSL2 + Docker boring. Each task includes: command, what the output means, and what decision you make next.

Task 1: Confirm WSL versions and default distro

cr0x@server:~$ wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu-22.04           Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2

Meaning: You have Ubuntu on WSL2, plus Docker Desktop’s internal distros. That’s normal for Model A.

Decision: If you don’t want Docker Desktop, you should not see docker-desktop distros. If you do, you’re in Model A whether you admit it or not.

Task 2: Check the WSL kernel version (sanity + bug context)

cr0x@server:~$ wsl --status
Default Distribution: Ubuntu-22.04
Default Version: 2
WSL version: 2.1.5.0
Kernel version: 5.15.133.1-1
WSLg version: 1.0.59
MSRDC version: 1.2.4677
Direct3D version: 1.611.1-81528511
DXCore version: 10.0.26091.1-240325-1447.ge-release
Windows version: 10.0.22631.3085

Meaning: Modern WSL with a 5.15-based kernel. Many older networking and systemd issues depend on kernel/WSL version.

Decision: If you’re on a much older WSL version, upgrade first before chasing ghosts. Debugging old WSL builds is paying interest on technical debt.

Task 3: Verify Docker ownership (Desktop vs in-distro)

cr0x@server:~$ docker context ls
NAME                DESCRIPTION                               DOCKER ENDPOINT                  ERROR
default *           Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
desktop-linux       Docker Desktop                            unix:///var/run/docker.sock

Meaning: Contexts exist; in WSL, both can point to a Unix socket depending on integration.

Decision: Pick a context and stick to it per workflow. If contexts flip under you, that’s a configuration problem, not a “Docker is flaky” problem.

Task 4: Check if systemd is enabled in the WSL distro (Model B prerequisite)

cr0x@server:~$ cat /etc/wsl.conf
[boot]
systemd=true

Meaning: systemd is enabled. You can run docker.service properly inside WSL (Model B).

Decision: If you want Model B and this is missing, add it and restart WSL. If you want Model A (Docker Desktop), you typically don’t need dockerd inside the distro at all.

Task 5: Restart WSL cleanly after config changes

cr0x@server:~$ wsl --shutdown
The operation completed successfully.

Meaning: All WSL VMs stopped. Next launch will re-read config.

Decision: Do this after changing .wslconfig or /etc/wsl.conf. If you don’t, you’re debugging a configuration you didn’t actually apply.

Task 6: Measure where your repo lives (Linux vs Windows mount)

cr0x@server:~$ pwd
/home/cr0x/src/myapp

Meaning: Repo is in the Linux filesystem. Good for Docker build contexts and bind mounts.

Decision: If you’re under /mnt/c/..., expect slower I/O. Move the repo unless you have a hard requirement to keep it on Windows.

Task 7: Demonstrate the performance boundary quickly (fio)

cr0x@server:~$ fio --name=randread --filename=/home/cr0x/fio.test --size=256M --bs=4k --rw=randread --iodepth=32 --numjobs=1 --runtime=10 --time_based --direct=1
randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=32
fio-3.33
Starting 1 process
randread: Laying out IO file (1 file / 256MiB)
randread: (groupid=0, jobs=1): err= 0: pid=221: Fri Feb  2 10:01:16 2026
  read: IOPS=21.4k, BW=83.5MiB/s (87.6MB/s)(835MiB/10000msec)

Meaning: You’re getting healthy random read IOPS on the Linux filesystem.

Decision: Run the same test on /mnt/c. If IOPS crater, stop blaming Docker and stop building on Windows mounts.

Task 8: Check Docker storage driver and root dir (where your layers actually live)

cr0x@server:~$ docker info --format 'RootDir={{.DockerRootDir}} Driver={{.Driver}} Cgroup={{.CgroupDriver}}'
RootDir=/var/lib/docker Driver=overlay2 Cgroup=systemd

Meaning: Docker uses overlay2 and stores data in /var/lib/docker inside the Linux environment that owns the daemon.

Decision: If you see an unexpected RootDir or a driver you didn’t plan for, you’re not running the daemon you think you are.

Task 9: Find where the VHDX lives on Windows (capacity planning)

cr0x@server:~$ powershell.exe -NoProfile -Command "Get-ChildItem -Recurse $env:LOCALAPPDATA\Packages\*Ubuntu*\LocalState\ext4.vhdx | Select-Object FullName,Length"
FullName                                                             Length
--------                                                             ------
C:\Users\cr0x\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc\LocalState\ext4.vhdx  64424509440

Meaning: Your Ubuntu distro’s virtual disk is 60 GiB on C:. That’s where your Linux filesystem lives.

Decision: If C: is small or protected by corporate policies, plan a controlled move using export/import (later tasks). Randomly “moving folders” is how you create unrecoverable WSL states.

Task 10: Check WSL disk usage from inside Linux (before you “clean up”)

cr0x@server:~$ df -hT /
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdb       ext4   251G   96G  143G  41% /

Meaning: The Linux root filesystem has space. This is inside the VHDX.

Decision: If Use% is high, clean inside Linux first (Docker prune, log cleanup). Don’t assume Windows sees freed space afterward.

Task 11: Identify Docker’s biggest space consumers (images/volumes)

cr0x@server:~$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          24        6         18.3GB    12.7GB (69%)
Containers      11        2         1.4GB     1.1GB (78%)
Local Volumes   13        5         22.9GB    9.6GB (41%)
Build Cache     0         0         0B        0B

Meaning: Volumes are the heavy hitters. Images are reclaimable too.

Decision: If you need to free disk, prune images/containers first, then audit volumes carefully. Pruning volumes blindly is how you “accidentally” delete someone’s local database.

Task 12: Safe-ish cleanup (prune with intent)

cr0x@server:~$ docker image prune -a
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:3f1e2a0f7c8d...
Total reclaimed space: 9.8GB

Meaning: Unused images removed, ~10 GB back inside Docker’s storage.

Decision: If your builds are slow, pruning won’t help. If your disk is full, pruning helps. Different problems, different levers.

Task 13: Verify bind mount performance risk (are you mounting Windows paths?)

cr0x@server:~$ docker run --rm -v /mnt/c/Users/cr0x/src:/src alpine ls -la /src | head
total 64
drwxrwxrwx    1 root     root          4096 Feb  2 10:22 .
drwxr-xr-x    1 root     root          4096 Feb  2 10:22 ..
-rwxrwxrwx    1 root     root          1962 Feb  1 19:04 README.md
-rwxrwxrwx    1 root     root           372 Feb  1 19:04 docker-compose.yml

Meaning: You’re bind-mounting a Windows path. It works, but it’s a performance trap for heavy I/O workloads.

Decision: If your stack does lots of file reads (Node, PHP, webpack, Python), move the repo into Linux and mount from /home instead.

Task 14: Confirm port reachability from Windows to WSL containers

cr0x@server:~$ docker run --rm -d -p 8080:80 nginx:alpine
c7c51d1c65b98a0c2a5c2b9d9a3a9b10f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6
cr0x@server:~$ powershell.exe -NoProfile -Command "Test-NetConnection -ComputerName 127.0.0.1 -Port 8080 | Select-Object TcpTestSucceeded"
TcpTestSucceeded
----------------
True

Meaning: Port publishing works and Windows can reach it on localhost.

Decision: If this fails under VPN, the issue is usually name resolution, firewall rules, or WSL NAT behavior—not “nginx is broken.”

Task 15: Diagnose DNS inside WSL (the silent productivity killer)

cr0x@server:~$ cat /etc/resolv.conf
nameserver 172.22.48.1
search corp.example
cr0x@server:~$ getent hosts registry-1.docker.io | head -n 1
2600:1f18:2148:bc02:7fd3:1c63:6f57:fc3a registry-1.docker.io

Meaning: WSL is using a NAT-provided resolver; DNS lookups work.

Decision: If lookups hang or return wrong results on VPN, you may need to disable auto-generated resolv.conf and set DNS explicitly (with care—see common mistakes).

Task 16: Export a distro before risky changes (your “undo” button)

cr0x@server:~$ wsl --export Ubuntu-22.04 D:\wsl-backups\Ubuntu-22.04.tar
The operation completed successfully.

Meaning: You now have an offline backup you can import elsewhere.

Decision: Do this before moving disks, reinstalling, or “cleaning up WSL.” If you don’t, you’re betting your work on luck.

Task 17: Move a distro to another drive (the correct way)

cr0x@server:~$ wsl --unregister Ubuntu-22.04
The operation completed successfully.
cr0x@server:~$ wsl --import Ubuntu-22.04 D:\wsl\Ubuntu-22.04 D:\wsl-backups\Ubuntu-22.04.tar --version 2
The operation completed successfully.

Meaning: The distro is now stored under D:\wsl\Ubuntu-22.04 (including its new VHDX). This is how you relocate WSL storage without corruption.

Decision: Do this if C: is constrained or if corporate encryption policies make WSL disk I/O miserable on the system drive.

Task 18: Verify Docker still points where you think after moves

cr0x@server:~$ wsl -d Ubuntu-22.04 -- bash -lc "docker ps"
CONTAINER ID   IMAGE           COMMAND                  CREATED       STATUS       PORTS                  NAMES
c7c51d1c65b9   nginx:alpine    "/docker-entrypoint.…"   2 min ago     Up 2 min     0.0.0.0:8080->80/tcp   nostalgic_borg

Meaning: Docker CLI can still talk to a daemon and your container state exists.

Decision: If containers/images “vanish,” you switched daemons or contexts. Diagnose ownership model drift first, not “Docker ate my homework.”

Fast diagnosis playbook

This is the order that finds the bottleneck quickly, without chasing folklore.

1) Identify the ownership model in 30 seconds

  • Check: wsl -l -v for docker-desktop distros; docker info for root dir and driver.
  • Why: If you’re unsure which daemon you’re talking to, every other observation is suspect.
  • Decision: Commit to Model A or Model B for this machine and team. “Both” is a debugging tax.

2) Measure the filesystem boundary (Linux vs /mnt/c)

  • Check: Where the repo lives (pwd), where bind mounts point, and do a quick fio test in both locations.
  • Why: Most “Docker on WSL is slow” reports are actually “I’m doing Linux builds on a Windows-mounted path.”
  • Decision: If /mnt/c is the hotspot, move code into Linux filesystem and adjust editor workflow.

3) Confirm resource limits (RAM/swap) and pressure signals

  • Check: WSL memory usage, swap behavior, and whether Windows is paging heavily.
  • Why: Under memory pressure, everything looks like I/O slowness.
  • Decision: Set .wslconfig limits; increase memory for build-heavy workloads; keep swap sane.

4) Validate DNS and proxy behavior (especially on VPN)

  • Check: getent hosts, pulls from registries, and whether /etc/resolv.conf is auto-generated.
  • Why: Build steps often fetch dependencies; broken DNS looks like “Docker build hangs.”
  • Decision: Fix DNS generation or set static DNS with a controlled policy.

5) Only then: Docker-specific performance (buildkit, cache, overlay2)

  • Check: Build logs, cache misses, layer invalidation patterns, and whether bind mounts are causing rebuild storms.
  • Why: Docker performance tuning is worthless if the underlying filesystem is wrong.
  • Decision: Tune Dockerfile ordering, use BuildKit, and reduce bind-mounted churn.

Common mistakes: symptom → root cause → fix

1) “Docker is painfully slow”

Symptom: npm install, composer install, pip install, or webpack builds take 5–20× longer than expected.

Root cause: Repo lives under /mnt/c and you’re bind-mounting it into containers; metadata operations are expensive across the Windows↔Linux boundary.

Fix: Move the repo into the Linux filesystem (/home/.../src). If you need Windows editor access, use VS Code Remote / WSL or access via \\wsl$\ from Windows.

2) “My images/containers disappeared after a reboot”

Symptom: Yesterday’s images are gone; docker ps is empty; volumes aren’t there.

Root cause: You’re talking to a different Docker daemon/context (Desktop vs in-distro), or Docker Desktop reset its data distro.

Fix: Run docker context ls, then docker info to confirm RootDir. Decide Model A or B and remove the other daemon to prevent drift.

3) “Docker build hangs on ‘Downloading…’”

Symptom: Builds stall fetching base images or dependencies; retry sometimes works.

Root cause: DNS resolution issues under VPN, or proxy settings not applied inside WSL/Docker environment.

Fix: Test name resolution with getent hosts. If needed, disable auto-generated resolv.conf and set known-good resolvers; align proxy env vars for both WSL and Docker.

4) “Published ports randomly stop working”

Symptom: -p 8080:80 worked earlier; now Windows can’t reach it on localhost.

Root cause: WSL NAT and firewall changes after sleep/VPN toggles; sometimes the WSL virtual switch is in a bad state.

Fix: Restart WSL (wsl --shutdown). If using Docker Desktop, restart it too. Re-test with Test-NetConnection.

5) “Disk is full but I deleted a ton of stuff”

Symptom: Windows drive space doesn’t increase after deleting Docker images or Linux files.

Root cause: VHDX does not shrink automatically; space remains allocated inside the virtual disk.

Fix: Clean up inside Linux, then compact the VHDX using supported tooling. If you can’t compact easily, export/import to a new VHDX as a pragmatic reset.

6) “File watching doesn’t work (hot reload broken)”

Symptom: App doesn’t reload on file changes; watchers miss events or spike CPU.

Root cause: Watching a Windows-mounted filesystem from Linux containers can degrade inotify semantics; or too many files overwhelm watchers.

Fix: Keep code in Linux filesystem; for large monorepos, configure polling with sane intervals as a fallback, but treat it as last resort.

7) “Git shows weird case-only renames or duplicates”

Symptom: CI passes on Linux but local behaves oddly; files differ only by case.

Root cause: Case sensitivity mismatch when working on Windows filesystem.

Fix: Work inside Linux filesystem for repos that assume Linux semantics; enforce case-consistent naming in the repo.

Three corporate mini-stories from the trenches

Incident: the wrong assumption (“WSL is basically just a folder”)

A product team rolled out WSL2 to standardize dev environments. The mandate was reasonable: Docker, Linux tooling, consistent builds. The implementation was not. Someone assumed WSL distros were just directories you could move around like any other cache.

So an engineer “saved space” by copying the WSL distro’s LocalState directory to D: and deleting the original from C:. It worked for a day, mostly. Then Docker Desktop updated, WSL registered state no longer matched the disk content, and the distro refused to start. The immediate symptom was “Docker won’t start,” but the real failure was “the VM disk is no longer where WSL thinks it is.”

The recovery was ugly. They had to unregister and re-import from whatever copy still existed. Some people lost local databases stored in volumes because nobody had a policy for exporting WSL state before surgery.

The lesson wasn’t “don’t use WSL.” It was: treat WSL distros like managed VM assets. If you want them on another drive, use export/import. If you want to reduce space, prune and compact, don’t play filesystem roulette.

Optimization that backfired: “Let’s put everything on /mnt/c so Windows tools can see it”

A different org wanted a smooth experience for Windows-first developers. Their idea: keep repos on C:\ so Windows security tools and IDEs “see everything,” then run builds in WSL2 and containers by bind-mounting the same Windows paths.

On small projects it looked fine. On the main monorepo it was a slow-motion incident. Incremental builds were no longer incremental. File scanning steps (linters, test discovery, TypeScript compilation) became the new bottleneck. People compensated by turning off checks locally—because deadlines don’t care about your architecture diagram.

Then came the real backfire: Docker builds started timing out in CI-like local scripts. Not because the CPU was weak, but because the build context tar and overlay operations were spending their time in the Windows↔Linux translation layer. Developers called it “Docker overhead.” It wasn’t. It was storage placement.

They eventually switched to Linux-native repo locations and used editor integrations to keep the Windows UX. The speedup was immediate and boring, which is the kind of success you want in operations.

Boring but correct practice that saved the day: “Export before changes”

A platform team maintained a standard WSL2 image for developers working on multiple containerized services. They had a rule: any change that might touch WSL distro storage requires an export first. No exceptions, no “it’ll be fine,” no heroics.

One quarter, a Windows update plus a security agent update caused disk I/O stalls on C:. Developers reported Docker builds taking forever, plus occasional WSL timeouts. The fix was to relocate distros to a different drive where the agent’s scanning policy was less aggressive. This is the kind of sentence that makes everyone sigh, but it happens.

Because they had a recent export, the move was procedural: shutdown WSL, export, unregister, import to the new location, verify. They didn’t have to debug corrupted VHDX files at 2 a.m. They also didn’t have to ask hundreds of engineers to rebuild environments from scratch.

It wasn’t glamorous. It was disciplined. And it turned a potentially chaotic migration into a controlled maintenance task.

Checklists / step-by-step plan

Step 0: Decide your Docker model (don’t improvise later)

  • Choose Model A if you want the easiest desktop experience, GUI management, and don’t need Linux-native daemon control.
  • Choose Model B if you want Linux-like server behavior, explicit control of /var/lib/docker, and fewer Docker Desktop surprises.

Step 1: Install WSL2 cleanly and verify

  • Install WSL and a distro.
  • Run wsl -l -v and confirm VERSION=2.
  • Run wsl --status and confirm you’re on a modern WSL version.

Step 2: Configure WSL resources (the boring guardrails)

Create or edit %UserProfile%\.wslconfig on Windows. Example policy (adjust to your machine):

  • Memory: cap to something reasonable (e.g., 8–16 GB on dev laptops).
  • Swap: don’t set it to 0 unless you like OOM kills during builds.
  • Processors: cap if you want your fans to stop negotiating for hazard pay.

Then wsl --shutdown to apply.

Step 3: Put code in the right place

  • Create ~/src inside WSL.
  • Clone repos there.
  • Run Docker builds from there.

Step 4: Docker setup by model

Model A: Docker Desktop + WSL2 integration

  • Enable WSL2 backend in Docker Desktop settings.
  • Enable integration for your chosen distro.
  • Confirm docker ps works inside the distro.
  • Do not install and run a separate dockerd inside WSL unless you intentionally want two daemons.

Model B: Docker Engine inside WSL2

  • Enable systemd in /etc/wsl.conf.
  • Restart WSL.
  • Install Docker Engine packages inside the distro.
  • Start and enable docker via systemd; confirm docker info shows overlay2 and sane root dir.

Step 5: Make disaster recovery boring

  • Before major changes, export the distro: wsl --export.
  • For drive relocation: export → unregister → import.
  • Keep a policy for what is allowed to live in Docker volumes locally (databases, caches) and how to back it up if it matters.

Joke #2: If your only backup plan is “I’ll remember what I did,” congratulations—you’ve invented single-point-of-failure memory.

FAQ

1) Should I use Docker Desktop or Docker Engine inside WSL?

If you want the least friction on Windows, use Docker Desktop (Model A). If you want Linux-native control and predictable daemon behavior, run Docker Engine inside WSL (Model B). Pick one per machine.

2) Why is building from /mnt/c so much slower?

Because you’re crossing a filesystem boundary with metadata translation. Builds do lots of tiny file operations. Tiny operations are where the boundary costs show up.

3) Where are my Docker images actually stored?

Run docker info and look at DockerRootDir. In Docker Desktop, the backing storage is in its managed environment; in in-distro Docker, it’s usually /var/lib/docker inside that distro’s VHDX.

4) Can I move WSL to another drive safely?

Yes: export the distro to a tar, unregister it, then import it to a directory on the target drive. Do not manually copy the LocalState VHDX and hope.

5) Why does Windows disk space not come back after deleting files in WSL?

Because the VHDX file doesn’t automatically shrink. You freed space inside ext4, but Windows still sees the VHDX allocation. Use compaction workflows or export/import to recreate a smaller VHDX.

6) How do I know if “Docker is slow” is actually DNS?

If pulls/build steps hang on network fetches, test with getent hosts and a simple image pull. If name resolution is slow or wrong, fix DNS before touching Dockerfiles.

7) Is it safe to run two Docker daemons (Desktop + in-distro)?

It can be done, but it’s usually a self-inflicted outage. You’ll confuse contexts, ports, and image locations. If you must, document contexts and pin them per project.

8) Why do ports sometimes work only from Windows or only from WSL?

Because NAT and forwarding rules differ by direction. Also, VPN/firewall changes can break the implicit glue. Restart WSL and Docker Desktop, then re-test with a known container and Test-NetConnection.

9) What’s the safest way to handle databases in local Docker volumes?

Assume volumes are disposable unless you back them up. For anything important, script exports (e.g., pg_dump) to a known location inside Linux filesystem and/or a Windows backup path.

10) Does enabling systemd in WSL matter for Docker?

For Model B, yes: systemd makes Docker behave like it does on real Linux, including service lifecycle and cgroup driver alignment. For Model A, it’s optional.

Next steps (do these, not vibes)

  1. Decide your Docker model and remove ambiguity: Desktop integration or in-distro daemon.
  2. Move active repos into Linux filesystem and stop bind-mounting Windows paths for heavy builds.
  3. Add WSL resource limits so Windows stays responsive under load.
  4. Export your distro before changes like drive moves, reinstallations, or “cleanup.”
  5. Use the fast diagnosis playbook the next time performance tanks: ownership → filesystem boundary → resources → DNS → Docker tuning.

When WSL2 and Docker are set up correctly, they don’t feel like a “stack.” They feel like your machine is finally cooperating. That’s the goal: boring, fast, recoverable.

← Previous
Your SSD Is Slow for No Reason: The Storage Driver Mistake Behind It
Next →
Email TLS: Why ‘Valid Certificate’ Still Fails (And the Real Fix)

Leave a comment