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.
The first question when running Node.js on Windows: "Install in WSL2 Linux, or native on Windows?" Both work, but performance, tooling compatibility, and debugging experience differ dramatically. This guide compares the two environments, gives you a decision rule, and walks through both setups.
I believe the WSL2 path is the right default for most JavaScript/TypeScript projects on Windows. Not because native Windows Node is broken, but rather because the web toolchain — native modules, shell scripts, CI parity — assumes a Linux environment, and WSL2 is what makes that assumption true on a Windows machine. Because CI runs Linux, the fewer translation layers between your local environment and CI, the more trustworthy your local test results.
Audience: developers writing JavaScript/TypeScript on Windows 11. Assumes Windows initial setup is done.
TL;DR
| Scenario | Pick |
|---|---|
| Next.js · React · Express · NestJS · Vite · general web backend | WSL2 Linux Node |
| Electron · Tauri desktop apps | Native Windows Node |
| Company CI is Linux | WSL2 Linux Node |
| Depend on Windows-only native modules | Native Windows Node |
| Learning / onboarding | WSL2 Linux Node (matches Mac guides) |
Rule of thumb: production is Linux anyway. Developing in WSL2 matches production. Without a strong counter-reason, go WSL2.
Prerequisites
- Windows 11 22H2+
- WSL2 + Ubuntu 24.04 (Windows initial setup)
- Windows Terminal (optional — windows-terminal-setup)
1. Native vs WSL2 — core differences
Performance
| Operation | Native Node | WSL2 Node |
|---|---|---|
npm install (1000 packages) | 25s | 15s |
next dev first compile | 5s | 2s |
| HMR (Hot Module Reload) | 800ms+ | 300ms |
jest 1000 tests | 25s | 18s |
| File watcher | Stable but slow | Fast but occasionally drops events |
WSL2's ext4 destroys NTFS on the "many small file reads" pattern Node generates. Caveat: mounting Windows files (
/mnt/c/...) inside WSL2 is slower than native — see §6.2.
Tooling compatibility
| Tool | Native Windows | WSL2 |
|---|---|---|
node-gyp builds (native modules) | Needs Visual Studio Build Tools | Standard build-essential |
sharp · canvas · node-sass | Often missing prebuilt binaries | Always works |
playwright | Works | Works (Linux browsers) |
puppeteer | Works | Works (separate Chrome install) |
| Docker | Through Docker Desktop | docker CLI directly |
Debugging
| Aspect | Native | WSL2 |
|---|---|---|
| VS Code debugger | Works out of the box | Needs Remote-WSL extension |
| Chrome DevTools | localhost:3000 directly | Auto-forwarded WSL2 → Windows |
| Env vars | .env works | .env works |
| Path separator | C:\Users\... (backslash) | /home/... (forward slash) |
2. Decision table
Pick WSL2 if
- Production is Linux — Vercel / AWS / GCP / Heroku covers 99% of cases
- Heavy use of
node-gypmodules —sharp,canvas,@tensorflow/tfjs-node - You want a fast dev server + HMR — the biggest day-to-day differentiator
- CI runs Ubuntu/Alpine — testing on Linux ensures CI passes
- You collaborate with Mac users — nearly identical (POSIX) environment
Pick native Windows if
- Building Electron desktop apps — producing Windows binaries
- Tauri Windows builds — Rust + WebView2
- Windows-only APIs — COM, Registry access
- Simple automation scripts — Notepad automation, etc.
- VS Code Remote-WSL feels too slow on a low-spec machine
Or use both
Common: WSL2 for work, native for side desktop apps. No conflict — separate PATHs.
3. WSL2 Node.js setup (recommended)
3.1 Enter WSL Ubuntu
wsl -d Ubuntu-24.043.2 mise for Node (recommended)
# Install mise (language version manager)
curl https://mise.run | sh
echo 'eval "$(/home/$USER/.local/bin/mise activate bash)"' >> ~/.bashrc
exec bash
# Install Node
mise use --global node@22
node --version # v22.xMore on mise: mac/dev-toolchain — works the same on Linux.
3.3 Or nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
exec bash
nvm install --lts
nvm use --lts3.4 Or apt (system package)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs⚠️ apt makes version switching awkward and depends on sudo. Prefer mise or nvm.
3.5 pnpm · yarn
npm install -g pnpm yarn3.6 Verify
node --version # v22.x
npm --version
pnpm --version
which node # ~/.local/share/mise/installs/node/22/bin/node (with mise)4. WSL2 + VS Code integration
4.1 Remote-WSL extension
VS Code Extensions:
- WSL (
ms-vscode-remote.remote-wsl)
After installing, command palette → WSL: Connect to WSL → pick Ubuntu.
Or, from inside WSL Ubuntu:
cd ~/projects/myapp
code .VS Code opens on Windows; the backend runs in WSL2.
4.2 Effect
node/npm/pnpmexecute inside WSL2- Files live on WSL2's ext4 (
~/projects/myapp) - The terminal is WSL2 bash
- Debugger and extensions run on the WSL2 side
Don't work under the Windows filesystem (
C:\) — performance plummets.
5. Native Windows Node.js setup
5.1 winget install
# Latest LTS
winget install OpenJS.NodeJS.LTS
node --version # v22.x5.2 Or fnm (version manager)
winget install Schniz.fnm
# Auto-activate from PowerShell profile
notepad $PROFILE
# Add:
# fnm env --use-on-cd | Out-String | Invoke-ExpressionAuto-switches based on .nvmrc / package.json#engines.node.
5.3 Or NVM for Windows
winget install CoreyButler.NVMforWindows
nvm install lts
nvm use lts
nvm-windowsis a separate project from Unixnvm. Commands are similar.
5.4 Build tools (for compiling native modules)
To build sharp / canvas / bcrypt etc.:
winget install Microsoft.VisualStudio.2022.BuildToolsOr:
npm install -g windows-build-tools # deprecated — prefer Visual Studio Build Tools5.5 Verify
node --version
npm --version
pnpm --version6. Frequent pitfalls
6.1 Both installed → confused which one runs
WSL2 has its own node, Windows has its own node — different versions per terminal. Fix:
- Inside WSL bash, WSL's Node (
which node→~/.local/share/mise/...) - In PowerShell, native Node (
Get-Command node→C:\Program Files\nodejs\...) - Pin the VS Code terminal default (
terminal.integrated.defaultProfile.windows:Ubuntu (WSL))
6.2 Windows-filesystem mount is slow
Working from /mnt/c/Users/me/projects/myapp in WSL2 → 5-10× slower I/O.
# ❌ Slow
cd /mnt/c/Users/me/projects/myapp
npm install
# ✅ Fast
cp -r /mnt/c/Users/me/projects/myapp ~/projects/
cd ~/projects/myapp
npm installMigrate any existing Windows-folder project to WSL2 once.
6.3 Line endings (CRLF vs LF)
Edit in WSL2 → commit → open the same file in Windows → CRLF gets injected. Fix:
git config --global core.autocrlf input # inside WSL2More: /multi-os/git-line-endings.
6.4 EACCES on npm install -g
# Inside WSL2 — change the npm prefix
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH=$HOME/.npm-global/bin:$PATH' >> ~/.bashrc
exec bashOn native Windows, use an admin PowerShell.
7. Verify (whichever environment you picked)
# WSL2 or PowerShell
node --version
npm --version
# Create package.json + install dep + run
mkdir hello-node && cd hello-node
npm init -y
npm install express
cat > index.js <<'EOF'
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('hello'));
app.listen(3000, () => console.log('http://localhost:3000'));
EOF
node index.js
# → browser localhost:3000 → "hello"If that works, setup is done.
8. Troubleshooting
Cannot find module 'sharp' (or another native module)
- Native Windows: install Visual Studio Build Tools, then
npm rebuild - WSL2:
sudo apt install build-essential, thennpm rebuild
Dev server unreachable from Windows browser (WSL2)
- Make the app bind
0.0.0.0or::(e.g.,next dev -H 0.0.0.0) - Windows 11 22H2+ auto-forwards; earlier versions reach via
localhost
pnpm command not found (PATH issue)
- Find where
npm install -gputs binaries and add it to PATH - Native:
npm config get prefix→<prefix>\bin - WSL2:
$HOME/.npm-global/binor the mise path
node-gyp build "Python not found"
- Native Windows:
winget install Python.Python.3.12 - WSL2:
sudo apt install python3 python3-pip
Can I build Electron from WSL2?
Possible, but complex. Electron's packaging produces host-OS artifacts (.exe / .app / .deb), so Windows builds belong on native Windows and Linux builds belong in WSL2. For Electron, prefer native Windows.
Next.js dev server keeps restarting (WSL2)
Don't work under /mnt/c/ (§6.2). Also use turbopack or set webpackDevMiddleware options in next.config.ts.
9. What's next
- Docker on WSL2 — /windows/docker-wsl2
- WSL tuning (memory · CPU · network) — /windows/wsl-tuning
- Windows initial setup — /windows/initial-setup
- Same setup on Mac — /mac/dev-toolchain (mise works identically)
References
Changelog
- 2026-05-16: First draft. WSL vs Native comparison + both setup paths + four pitfalls + six troubleshooting cases.
Keep reading
- 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.
- 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.