WSL2 Optimization

Speed Up File I/O: Where to Keep Code—Linux vs Windows Paths

Developers on Windows increasingly rely on WSL2 to get a fast, native-feeling Linux experience. Yet many hit a wall when projects slow to a crawl during package installs, builds, Docker bind-mounts, or Git operations. The culprit is almost always the same: where your code lives and the path you access it from. This article explains exactly how to speed up file I/O by choosing the right place to keep your project—Linux vs Windows paths—plus how to tune WSL2 for consistently fast, reliable performance. By the end, you’ll know where to put your code, how to configure WSL2, and how to avoid cross-filesystem bottlenecks across Node.js, Python, Docker, databases, and more.

H2: Overview: Why path location matters in WSL2
WSL2 runs a real Linux kernel in a lightweight VM. That VM exposes two main filesystems:

  • Linux filesystem (inside the VM): e.g., /home//project and \wsl$\DistroName\home\\project from Windows Explorer. Backed by an ext4 virtual disk (ext4.vhdx).
  • Windows filesystem (mounted into Linux): e.g., /mnt/c/Users//project. Backed by NTFS via drvfs with translation layers for metadata and case-sensitivity.

Accessing Linux files from Linux processes is near-native. But crossing the boundary—Linux tools reading/writing Windows paths under /mnt/c (or Windows tools watching/editing files located inside WSL)—introduces a translation layer that can be orders of magnitude slower for metadata-heavy workloads. Typical symptoms:

  • npm install, pip install, yarn, pnpm, cargo, Maven/Gradle builds are much slower when the working directory is under /mnt/c.
  • Git commands such as status, checkout, and rebase are very slow on Windows-mounted paths.
  • Docker bind mounts using Windows paths into Linux containers perform poorly.
  • File watchers and live-reload tools miss events or lag when the watched folders are on the “wrong” side.

Rule of thumb: Run Linux tools against files located on the Linux filesystem. Use VS Code Remote – WSL to edit those files. Only cross the boundary when necessary.

H2: Quick Reference Table

Command or File Purpose Example Output / Snippet
wsl -l -v List distros and version NAME STATE VERSION Ubuntu-22.04 Running 2
wsl –status Show WSL status and defaults Default Version: 2 Kernel version: 5.15.x
wsl –update Update WSL kernel and components WSL is up to date
wsl –shutdown Stop all WSL VMs (apply config changes) (no output)
code . (inside WSL) Open VS Code on Linux files via Remote – WSL [VS Code window opens attached to WSL]
wslpath -w . Convert Linux to Windows path \wsl.localhost\Ubuntu-22.04\home\user\project
mount Show mount points and options C: on /mnt/c type drvfs (rw,metadata,…)
cat /etc/os-release Verify distro details NAME=”Ubuntu” VERSION=”22.04 LTS”
docker info Verify Docker Desktop with WSL2 backend Storage Driver: overlay2 (Backed by WSL2)
cat ~/.wslconfig (Windows side: %UserProfile%.wslconfig) Configure WSL2 VM resources [wsl2] memory=8GB processors=4 swap=2GB
cat /etc/wsl.conf Automount and DNS settings inside Linux [automount] options=metadata,case=dir

H2: Key Concepts & Prerequisites
H3: Requirements

  • Windows: Windows 11 (preferred) or Windows 10 2004+ with latest updates.
  • WSL: WSL2 installed and set as default (wsl –install on supported systems).
  • Permissions: Administrator rights to install components and edit system-wide settings.
  • Tools: VS Code with the Remote – WSL extension, Git, Docker Desktop (WSL2 backend), language runtimes (Node.js, Python, Java, etc.) installed in WSL where possible.
  • Hardware: Virtualization enabled in BIOS/UEFI; adequate RAM/SSD. Allow 10–20 GB free disk for dev workloads.

H3: Key terms you’ll see here

  • WSL2: The second-generation Windows Subsystem for Linux with a real Linux kernel in a VM.
  • ext4.vhdx: The virtual disk file storing your Linux filesystem.
  • .wslconfig: Global WSL2 configuration file on Windows that controls VM resources.
  • /etc/wsl.conf: Per-distro Linux-side configuration for automounting, networking, and more.
  • Docker (WSL2 backend): Docker Desktop using WSL2’s virtualization for Linux containers.
See also  How to Shrink and Compact ext4.vhdx Without Data Loss

H2: Step-by-Step Guide
H3: 1) Verify you are on WSL2 and up to date

  • Open PowerShell (non-admin is fine) and run:

wsl -l -v
wsl –status
wsl –update

Ensure your distro shows VERSION 2. If not:

wsl –set-version 2

Then stop WSL so updates take effect:

wsl –shutdown

H3: 2) Decide where to keep your code

  • Best performance: Keep your repo under the Linux filesystem, e.g., /home//project.
  • Access from Windows tools: Use \wsl$\DistroName\home\\project (or \wsl.localhost\DistroName…) in Explorer.
  • Avoid storing your project under /mnt/c when running Linux tools against it.

H3: 3) Move an existing project into the Linux filesystem
Inside WSL:

mkdir -p ~/dev

If your repo currently lives on /mnt/c/Users//dev/myapp:

rsync -a –info=progress2 /mnt/c/Users//dev/myapp/ ~/dev/myapp/

or use git to reclone directly into Linux

cd ~/dev
git clone https://github.com/org/myapp.git

Fix ownership and permissions if needed:

sudo chown -R $USER:$USER ~/dev/myapp

Open in VS Code (Remote – WSL):

cd ~/dev/myapp
code .

H3: 4) Configure VS Code to edit Linux files

  • Install the “Remote – WSL” extension in VS Code on Windows.
  • From a WSL terminal, run code . while in the Linux directory.
  • This ensures your editor operates inside WSL on Linux paths, avoiding cross-OS overhead.

H3: 5) Tune WSL resources with .wslconfig
Create or edit %UserProfile%.wslconfig on Windows:

[wsl2]
memory=8GB
processors=4
swap=2GB
localhostForwarding=true

Guidance:

  • memory: Cap VM memory so it doesn’t starve Windows, but leave enough for builds/containers.
  • processors: Limit logical cores if you need to preserve Windows responsiveness.
  • swap: Some swap helps large builds but keep it moderate on SSDs.

Apply changes:

wsl –shutdown

Restart a WSL shell.

H3: 6) Improve Windows automount behavior (optional)
Inside WSL, set /etc/wsl.conf to control /mnt mounts:

sudo nano /etc/wsl.conf

Example:

[automount]
enabled = true
options = “metadata,case=dir”
mountFsTab = false

  • metadata preserves Unix permissions on Windows-mounted paths (useful for mixed workflows, slightly more overhead).
  • case=dir allows per-directory case-sensitivity on drvfs.

Apply:

wsl –shutdown

H3: 7) Make Docker fast with Linux paths

  • Ensure Docker Desktop uses the WSL2 backend (Docker Desktop Settings > General).
  • Put your project under Linux paths and mount those into containers:

docker run –rm -v ~/dev/myapp:/app -w /app node:20 npm ci

  • In docker-compose.yml:

services:
web:
image: node:20
working_dir: /app
volumes:

  • ${HOME}/dev/myapp:/app

Avoid: -v /mnt/c/Users//myapp:/app because I/O will be slow.

H3: 8) Path conversion when you must cross boundaries

  • From WSL to Windows path:

wslpath -w .

-> \wsl.localhost\Ubuntu-22.04\home\user\dev\myapp

  • From Windows to WSL, simply navigate to \wsl$\DistroName\home\\dev\myapp in Explorer.

H3: 9) Keep Git fast
Inside WSL:

git config –global core.autocrlf input
git config –global core.filemode false

  • Store repos on Linux paths. If you must use /mnt/c, expect slowdowns for large repos.
  • For massive repos, consider sparse checkout or partial clone features.

H3: 10) Optional: Reduce antivirus overhead
Accessing WSL files from Windows can be slowed by real-time scanning. If policies allow, add a Windows Defender exclusion for the Linux VHDX or WSL network share:

  • ext4.vhdx path: C:\Users\\AppData\Local\Packages\\LocalState\ext4.vhdx
  • Or the \wsl$\DistroName path.
    Security note: Exclusions reduce protection. Only exclude what you trust.
See also  Fix DNS/Network Issues Between Windows and WSL2 (Reliable Methods)

H2: Troubleshooting
H3: DNS not working (especially on corporate VPN)
Symptoms: apt update fails, npm install cannot resolve hosts.

Fix A: Let WSL generate resolv.conf but reapply after VPN switches

  • Default behavior uses a dynamic resolv.conf. Restart WSL after VPN changes:

wsl –shutdown

Fix B: Pin a custom DNS

  • Inside WSL:

sudo bash -c ‘echo -e “[network]\ngenerateResolvConf=false” > /etc/wsl.conf’
sudo rm /etc/resolv.conf
echo “nameserver 1.1.1.1” | sudo tee /etc/resolv.conf
wsl –shutdown

Use your corporate DNS as required.

H3: Git is slow or hangs

  • Move the repo from /mnt/c/… to ~/… (Linux filesystem).
  • Disable aggressive virus scanning on dev folders (if allowed).
  • Check line-ending config:

git config –global core.autocrlf input

H3: File I/O slowness during builds (Node, Python, Java)

  • Confirm you are running on the Linux filesystem (pwd should start with /home).
  • Ensure Docker bind mounts use Linux paths.
  • Avoid polling-based watchers where possible; native inotify works best on Linux paths.

H3: Disk space bloat in ext4.vhdx
Symptoms: Linux VHD grows and does not shrink after deleting files.

Fix (export/import compaction):

wsl –shutdown

In PowerShell:

wsl –export D:\backup.tar
wsl –unregister
wsl –import D:\WSL\ D:\backup.tar –version 2

Or use Optimize-VHD if Hyper-V tools are installed (path varies per distro):

PowerShell (Admin)

Stop-Process -Name “vmmem” -ErrorAction SilentlyContinue
wsl –shutdown
Optimize-VHD -Path “$env:LOCALAPPDATA\Packages\\LocalState\ext4.vhdx” -Mode Full

H3: Permission errors on Windows-mounted paths

  • Mount with metadata for Unix-like permissions:

In /etc/wsl.conf:

[automount]
options = “metadata,case=dir”

  • Or keep executables and build outputs on Linux paths.

H3: Docker bind mounts are slow or unresponsive

  • Switch volume paths to Linux locations:

  • ${HOME}/dev/myapp:/app

  • Confirm Docker uses WSL2 backend:

docker info | findstr WSL

H2: Optimization & Best Practices
H3: Always run Linux workloads on Linux paths

  • Keep projects under /home/ or another directory on the Linux filesystem.
  • Access them from Windows via \wsl$\DistroName… using VS Code Remote – WSL.

H3: Avoid cross-boundary hot paths

  • Don’t bind-mount /mnt/c paths into containers; use Linux paths.
  • Don’t run npm install/pip install in /mnt/c repos.
  • Don’t rely on Windows-based file watchers for Linux builds.

H3: Use proper file watchers

  • Many Node tools (webpack, Vite, Next.js) depend on inotify. They perform best on Linux paths.
  • Avoid forcing polling (e.g., CHOKIDAR_USEPOLLING=1) unless absolutely necessary.

H3: Tune WSL2 resources for your workload

  • .wslconfig memory and processors settings can stabilize performance under load.
  • Provide a modest swap to avoid OOM during large compiles.
  • Restart WSL after config changes to reclaim memory:

wsl –shutdown

H3: Keep WSL2 updated

  • Newer WSL releases improve networking, filesystem, and resource management:

wsl –update

H3: Cache smartly

  • For package managers: Keep caches on Linux paths (default if your project is there).
  • Docker: Prefer BuildKit and layer caching; avoid copying huge node_modules repeatedly if you can leverage .dockerignore and multi-stage builds.

H3: When you must use Windows paths

  • Enable [automount] options=metadata in /etc/wsl.conf for better permissions mapping.
  • Expect slower I/O; use it only for artifacts that must remain on NTFS (e.g., tools run natively on Windows).

H2: Project-Based Example: Speeding up a Node.js + PostgreSQL workflow
Goal: Optimize a typical full-stack app with Node.js backend, React frontend, and a PostgreSQL database running in Docker.

See also  Run Databases in WSL2 (Postgres/MySQL/Mongo) Without Performance Regret

H3: 1) Place code on Linux filesystem
Inside WSL:

mkdir -p ~/dev/myapp
cd ~/dev
git clone https://github.com/org/myapp.git
cd myapp

H3: 2) Open with VS Code Remote – WSL

code .

VS Code connects to WSL; extensions run in Linux context. Editing is snappy without crossing the filesystem boundary.

H3: 3) Fast package install

npm ci

or

pnpm i

or

yarn install

Because files are on Linux paths, metadata-heavy operations complete much faster than on /mnt/c.

H3: 4) Dockerized PostgreSQL with Linux bind mounts
docker-compose.yml excerpt:

version: “3.9”
services:
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: appdb
volumes:

  • ${HOME}/dev/myapp/.data/postgres:/var/lib/postgresql/data
    ports:
  • “5432:5432”
    api:
    image: node:20
    working_dir: /app
    volumes:
  • ${HOME}/dev/myapp:/app
    command: bash -lc “npm ci && npm run dev”
    depends_on:
  • db

Run:

docker compose up

Both the app and database use Linux path volumes—fast and reliable.

H3: 5) Database tools and migrations

  • Use psql from WSL:

psql -h localhost -U app -d appdb

  • Run migrations (e.g., Prisma, Sequelize, Knex) from within WSL so they operate on Linux paths.

H3: 6) Optional Windows access

  • If you need to open a file from Windows Explorer:
    Open \wsl$\Ubuntu-22.04\home\user\dev\myapp
    This provides convenient read/write without moving the project.

Expected gains:

  • npm/pnpm/yarn: Often 2x–10x faster vs /mnt/c
  • Docker bind mounts: Far more responsive; fewer file-change issues
  • Git operations: Large repos feel snappy; status/checkout significantly faster

H2: Conclusion
The single most effective way to speed up WSL2 file I/O is to keep your code on the Linux filesystem and access it with Linux tools. Avoid crossing the Linux/Windows boundary for hot paths like installs, builds, Git operations, and Docker bind mounts. Combine that with a thoughtful .wslconfig, up-to-date WSL2, and selective use of Windows integrations, and you’ll get a fast, stable, and enjoyable dev experience. Speed Up File I/O: Where to Keep Code—Linux vs Windows Paths isn’t just a setting—it’s a workflow choice that unlocks the performance WSL2 was designed to deliver.

H2: FAQ
H4: Should I always keep my repo in /home instead of /mnt/c?
Yes, if you are running Linux tools (Node, Python, Docker, compilers) against it. Keeping the repo in /home (Linux filesystem) avoids the drvfs translation overhead and is consistently faster.

H4: Can I still open and edit Linux files from Windows?
Yes. Use \wsl$\DistroName\home\\project or \wsl.localhost\DistroName… in Explorer, or open the folder via VS Code Remote – WSL. You’ll edit the same files without moving them to Windows.

H4: Why are Docker bind mounts slow with Windows paths?
When you mount /mnt/c/… into a Linux container, every filesystem operation crosses the WSL boundary. This adds latency and hurts metadata-heavy workloads. Mount Linux paths like ${HOME}/project instead.

H4: Is there any way to make /mnt/c faster?
You can tweak /etc/wsl.conf with options=metadata,case=dir and exclude folders from antivirus scanning, but it will still be slower than native Linux paths for heavy I/O. Use /mnt/c only when you must.

H4: How do I compact my growing ext4.vhdx?
Export/import the distro (wsl –export, –unregister, –import) or use Optimize-VHD with Hyper-V tools installed. Always wsl –shutdown first and back up important data.

Thanks for reading—now go place your code where it runs fastest and enjoy a noticeably snappier WSL2 workflow!

About the author

Jonathan Dudamel

Jonathan Dudamel

I'm Jonathan Dudamel, an experienced IT specialist and network engineer passionate about all things Windows. I have deep expertise in Microsoft project management, virtualization (VMware ESXi and Hyper-V), and Microsoft’s hybrid platform. I'm also skilled with Microsoft O365, Azure ADDS, and Windows Server environments from 2003 through 2022.

My strengths include Microsoft network infrastructure, VMware platforms, CMMS, ERP systems, and server administration (2016/2022).