DEV Community

Sho Naka
Sho Naka

Posted on

Why WSL2 Is Slow on /mnt/c, and How to Find the Exact Operation Costing You Time

cp crawls. npm install takes forever. The exact same command, run one directory over, is instant.

If you've used WSL2 long enough, you may have hit this. This post covers what's actually causing it, and how to find the specific culprit in your own setup — once you know that, the fix is usually obvious.

TL;DR: WSL2 itself isn't slow. Crossing the boundary between the Linux filesystem and the Windows filesystem (/mnt/c) carries a real, measurable cost, and that cost lands hardest on workloads made of many small operations — in my testing, creating 300 small files on the Windows side took an order of magnitude longer than the identical work on the Linux side. Below is a short A/B benchmark to prove this on your own machine, a way to see which syscalls are paying the cost, and fixes with their side effects included.

The answer up front

It's not WSL that's slow. Operations that cross the boundary between the Windows filesystem and the Linux filesystem carry real overhead, and that overhead hits hardest when a workload is made of many small, discrete operations rather than fewer, larger ones.

So the question isn't only "how do I make this faster." It's "where can I stop crossing so often."

Handle the same 1GB as a single large file, or as 1,000 small files, and the many-small-files case can lose by an order of magnitude — even though the total data moved is identical.

What Microsoft actually says

Microsoft's own docs are direct about this:

Unless you have a specific reason to cross file systems, we recommend you store your files on the same file system as the tools you plan to use... If you are working with the Linux command line, we recommend storing your files in the Linux file system... If you are working with a Windows tool such as Notepad, we recommend storing your files in the Windows file system.

Source: Working across file systems

Worth noting: that's the fact and the recommendation, and it's all this particular page says. It doesn't spell out why it's slow. Everything past this point is that "why" — pulled from GitHub issues, community analysis, and my own measurements — so I'm keeping the sourcing separate from here on.

The part this page doesn't explain

Reaching /mnt/c from WSL2 goes over a protocol called 9P. What follows is community analysis plus what I measured myself, not vendor documentation.

9P has a cap on how much data a single round trip can carry (the msize parameter). On the Windows-side client, that value currently isn't something you can change — it's fixed (microsoft/WSL#9125). If you've seen a guide claiming you can raise it to fix slowness, that may be describing the Linux-side mount options for a different setup — it doesn't apply to the Windows-side client.

What matters more than the cap itself is what it's a cap on: it applies per round trip, not per byte moved overall — so operations that generate a lot of small round trips (opening a file, closing it, checking whether it exists) accumulate that cost fast. Multiple GitHub issues describe the same pattern — workloads with lots of small, discrete file operations get hit hardest (#4197, #5103).

One more thing: you may see claims that WSL2 has since switched to a different, newer transport for this. In my environment, at the time of writing, it's still on the legacy 9P path. Don't take my word — or that claim — for it; the next section shows how to check your own environment.

Find the culprit in your own setup

This is the part that matters. Not general advice — the specific operation in your environment that's paying the cost.

Step 1: run the same thing on both sides, and time it

The simplest, most reliable test there is. Run identical work on the Linux side and the Windows side, and compare wall-clock time.

# Linux side
mkdir -p ~/bench && cd ~/bench
time (for i in $(seq 1 300); do echo x > "f$i"; done)
time (for i in $(seq 1 300); do rm -f "f$i"; done)

# Windows side (swap in a writable path under C:, e.g. C:\Users\<you>\bench)
mkdir -p /mnt/c/wsl-bench-test && cd /mnt/c/wsl-bench-test
time (for i in $(seq 1 300); do echo x > "f$i"; done)
time (for i in $(seq 1 300); do rm -f "f$i"; done)
Enter fullscreen mode Exit fullscreen mode

On my machine, both creation and deletion came out an order of magnitude slower on the Windows side. Creation showed the bigger gap — the Linux side finished those 300 files in a fraction of a second, the Windows side took several seconds.

Try the same comparison with one large file read instead of many small writes, too. Don't assume it's automatically cheap just because it's a single operation — run it and see. How the cost lands tends to depend on the specific operation, not just which side of the boundary you're on.

Step 2: find which syscalls are paying the cost

Once you know there's a gap, the next question is which operation is causing it.

# install if you don't have it
sudo apt-get install -y strace

strace -f -c -o /tmp/trace.txt <the command you're benchmarking>
cat /tmp/trace.txt
Enter fullscreen mode Exit fullscreen mode

Compare the per-call time for openat and newfstatat between the Linux-side run and the Windows-side run — not the call count, the cost per call. A syscall whose per-call cost spikes on the Windows side is a strong signal, not proof, of where the boundary tax is landing; it tells you where the time is going even without confirming that every single call physically crossed.

Step 3: count how often your own script actually crosses

# check which side a given directory is on
stat -f -c '%T' .
# v9fs      → Windows side (crosses the boundary)
# ext2/ext3 → Linux side (doesn't)

# count how many times a script crosses into /mnt/
strace -f -e trace=openat <your script> 2>&1 | grep -c '/mnt/'
Enter fullscreen mode Exit fullscreen mode

That's a heuristic count, not an exact tally of protocol round trips — it only catches openat calls whose traced path literally contains /mnt/, so it'll miss relative-path opens, other syscall types, and anything served from cache. Still, a high number is a strong hint that a lot of your script's cost lives right there.

Fixes, with their side effects

The baseline fix: keep working files on the Linux side

This is Microsoft's own recommendation, and it's the most reliable option: there's no boundary tax to pay if you never cross it for the files you touch constantly.

Anything you touch regularly from Linux commands goes in your WSL home directory, not /mnt/c. That alone makes most of this problem disappear — though if your workflow also leans on Windows-native tools against those same files, weigh that trade-off for your own setup.

When you genuinely have to cross

If you need to copy something that has to stay on the Windows side, PowerShell's Copy-Item is noticeably faster than WSL's cp for the same job — because it crosses the boundary far fewer times per file.

powershell.exe -NoProfile -Command "Copy-Item -Recurse '<source>' '<destination>'"
Enter fullscreen mode Exit fullscreen mode

But there's a real side effect, and it's specific to this exact move: copying a directory that lives on the WSL/Linux side, out to the Windows side, this way. In my testing, doing that with a directory containing a WSL-side symlink threw an error and left an empty file where the link should have been — symbolic links don't survive that particular trip correctly.

Don't run this on a Linux-side directory that contains symlinks. You'll trade speed for something quietly broken.

What's floating around but isn't actually usable (yet)

Raising the round-trip size limit isn't something you can do on the Windows-side client right now, for the reasons above. Switching to a newer transport also isn't the default in a normal setup. Treat both as unconfirmed for your setup, not as something to act on today.

Wrapping up

A large share of the slowness comes down to how many times an operation crosses the boundary between WSL2 and Windows — and that cost lands hardest on workloads built from lots of small operations.

Which means the fix isn't just "make it faster" — it's "stop making it cross so often." Keep working files on the Linux side. If you must cross, minimize how often.

And measure your own setup before you trust anyone else's numbers, mine included. Steps 1 and 3 above take just a few minutes altogether. Once you know the culprit, the fix tends to be obvious.


Sho Naka (nomurasan). Originally written in Japanese; this English version is an AI-assisted adaptation of my own piece, not a literal translation. The benchmark results and the symlink side effect are from my own testing on my own machine — your numbers may differ.

Top comments (0)