
WSL: Windows Strikes Back
Developers must use Mac? That was before WSL2. Planting Linux Kernel inside Windows.

Developers must use Mac? That was before WSL2. Planting Linux Kernel inside Windows.
Why does my server crash? OS's desperate struggle to manage limited memory. War against Fragmentation.

Two ways to escape a maze. Spread out wide (BFS) or dig deep (DFS)? Who finds the shortest path?

Fast by name. Partitioning around a Pivot. Why is it the standard library choice despite O(N²) worst case?

Establishing TCP connection is expensive. Reuse it for multiple requests.

Back in 2015, when I first started learning to code, all the senior developers around me carried MacBooks. I was a broke college freshman with a second-hand Samsung laptop running Windows. I wanted to code at Starbucks like "real developers," but somehow, bringing a Windows laptop felt like showing up to a chef's convention with instant ramen.
The problem wasn't vanity; it was the development environment. To learn backend development, I needed a Unix-like system. Windows wasn't Unix. Basic tools like bash, curl, grep, and ssh weren't available out of the box. You had to cobble together half-baked solutions like Cygwin or Git Bash. Servers ran Ubuntu, but I was developing on Windows, which led to countless "works on my machine" situations.
So I chose dual booting. I split my hard drive in half—Windows on one side, Ubuntu on the other. Gaming or writing documents? Boot Windows. Coding? Reboot into Ubuntu. It was the peak of inconvenience. Some days I rebooted my laptop five times. The Windows startup sound became my nemesis.
Later, I tried VMware Workstation. Running Ubuntu as a virtual machine meant no rebooting, but it was painfully slow. On my 4GB laptop, allocating 2GB to the VM meant both Windows and Linux crawled. Running a compile would make the fan scream like it was auditioning for a horror movie.
"When I make money, I'm buying a MacBook."
That was the collective lament of every Windows-using beginner developer.
Then, in 2016, Microsoft made a bizarre announcement: they were building something called Windows Subsystem for Linux (WSL). The idea was to let you run native Linux binaries on Windows. Everyone was suspicious. "Microsoft supporting Linux? What's the catch?" Historically, Microsoft had been hostile to Linux and open source.
But this wasn't a trap—it was a strategic pivot. Microsoft realized that to grow Azure in the cloud era, they needed to keep developers on Windows. If local development moved to Mac or Linux while servers ran Linux, the Windows ecosystem would die. CEO Satya Nadella declared "Microsoft loves Linux," and it wasn't empty rhetoric—over half the VMs running on Azure were Linux.
That's how WSL was born. WSL 1, released in 2016, was an interesting experiment, but it had serious problems.
To understand WSL 1's architecture, you need to grasp the concept of a translation layer. Linux programs use Linux system calls (syscalls)—functions like open(), read(), write(). Windows has a completely different kernel structure.
WSL 1 worked by translating Linux syscalls into Windows API calls in real-time. Like an interpreter. When a Linux program said "open this file," WSL caught it and translated it to "Hey Windows NT kernel, please open this file."
This was an impressive engineering feat, but it had fundamental limitations:
I tried WSL 1 and thought, "This is just slightly better Git Bash." The hype was high; the disappointment was higher.
When Microsoft announced WSL 2 in 2019, my reaction was, "Here we go again, what's different now?" But when I actually tried it, I was shocked. This was a completely different beast.
The core idea of WSL 2 is simple: Stop translating. Run a real Linux kernel.
Microsoft took the Linux kernel source code (it's open source, after all), customized it for Windows, and ran it in a lightweight virtual machine using Hyper-V, their hypervisor. This is the lightweight VM strategy.
The difference from traditional VMs:
It's like pitching a small tent inside your house. Inside the tent (Linux), it's a completely independent space, but you can still use the whole house (Windows). You share the bathroom (file system) and the Wi-Fi (network).
[Linux App]
↓ (syscall)
[WSL Translation Layer]
↓ (convert)
[Windows NT Kernel]
WSL 2 structure:
[Linux App]
↓ (syscall)
[Real Linux Kernel (in VM)]
↕ (Hyper-V)
[Windows NT Kernel]
See the difference? WSL 2 has a real Linux kernel. No translation.
Now let me walk you through actually installing WSL 2 and setting up a Node.js development environment. This is my actual setup.
If you're on Windows 10 version 2004 or later, it's a one-liner:
wsl --install
This command:
Reboot your computer, and Ubuntu installation completes. It asks for a username and password—this is your Linux account, separate from your Windows account.
If you want a specific distribution:
# List available distributions
wsl --list --online
# Install Ubuntu 22.04
wsl --install -d Ubuntu-22.04
First login to Ubuntu? Update packages:
sudo apt update && sudo apt upgrade -y
# Install essential dev tools
sudo apt install -y \
build-essential \
curl \
wget \
git \
vim \
zsh
Install zsh (more powerful than bash) and make it pretty with Oh My Zsh:
# Set zsh as default shell
chsh -s $(which zsh)
# Install Oh My Zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
# Add useful plugins
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
git clone https://github.com/zsh-users/zsh-syntax-highlighting ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
# Edit .zshrc to enable plugins
vim ~/.zshrc
# plugins=(git zsh-autosuggestions zsh-syntax-highlighting)
Restart your terminal and zsh kicks in. Command autocompletion actually works now.
Best practice is to use NVM (Node Version Manager):
# Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# Restart terminal or
source ~/.zshrc
# Install Node.js LTS
nvm install --lts
nvm use --lts
# Verify
node --version
npm --version
# Install global packages
npm install -g yarn pnpm typescript ts-node nodemon
Now let's create an actual project:
# Work from home directory inside WSL (important!)
cd ~
mkdir projects
cd projects
# Create Express.js project
mkdir my-api
cd my-api
npm init -y
npm install express
# Write server.js
cat > server.js << 'EOF'
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.json({
message: 'Hello from WSL2!',
platform: process.platform,
hostname: require('os').hostname()
});
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
EOF
# Run it
node server.js
Open http://localhost:3000 in your Windows browser and you get a response. A Linux server talking to a Windows browser. This works because networking is integrated.
This is where WSL gets truly magical:
# From WSL terminal
cd ~/projects/my-api
code .
Type that command and Windows VS Code opens, editing files inside WSL. VS Code automatically installs the "Remote - WSL" extension and actually runs a VS Code Server inside WSL. The GUI is in Windows, but the code execution is in Linux.
The terminal integrates too. Hit Ctrl+` and you get a WSL bash/zsh terminal. The debugger works. Extensions install. This is a native Linux development experience.
The biggest mistake people make with WSL 2 is where they put their files. This can make a 10x performance difference.
WSL 2 has two file systems:
/home/username/./mnt/c/Users/....You can access Windows files from WSL and vice versa:
�CB9�
Here's where massive performance differences happen:
For example, if you put a Node.js project in /mnt/c/Users/RATIA/projects/ and run npm install from WSL:
Result? Painfully slow. npm install can take 5 minutes.
Solution: Put projects inside WSL.
�CB10�
When files are in ~/ (Linux home), you're using ext4 directly. npm install takes under a minute.
WSL files are accessible from Windows Explorer at \\wsl$\Ubuntu\home\username\. Create a shortcut to this location. You can copy files to Windows for backup.
Docker is WSL 2's killer app. Before WSL 2, running Docker on Windows meant Docker Desktop had to spin up a separate Linux VM using VirtualBox or Hyper-V. Heavy and slow.
After WSL 2, Docker Desktop can use WSL 2 as its backend. This was a game changer:
docker commands work in Windows PowerShell and WSL�CB11�
Docker containers run inside WSL at native Linux speed. Kubernetes (kind, k3s) works great in WSL too.
For me, spinning up PostgreSQL + Redis + API server with Docker Compose used to take 5 minutes pre-WSL. Now it takes under 30 seconds.
WSL 2 is technically a VM, but networking is configured in NAT mode. What this means:
localhost:3000 in WSL? Windows can access it at localhost:3000Most of the time, "it just works." But occasionally:
To access a service running on Windows (like PostgreSQL) from WSL, sometimes you need the Windows IP instead of localhost:
�CB12�
Or create an alias in .bashrc/.zshrc:
�CB13�
WSL 2 supports direct NVIDIA GPU access. This is huge for deep learning developers using CUDA.
Setup:
�CB14�
TensorFlow and PyTorch leverage the GPU directly. Game on Windows, train models in WSL—both possible.
WSL used to not support systemd. This was a problem because many Linux services (PostgreSQL, Redis, nginx) are managed by systemd.
Since 2022, WSL officially supports systemd:
�CB15�
Now you can manage services like real Linux:
�CB16�
In 2021, WSLg was added, enabling Linux GUI applications in WSL. No X11 server installation needed—it just works.
�CB17�
This is based on Wayland and integrates with the Windows window system. Audio works. Clipboard is shared.
Practical uses:
WSL 2 isn't perfect. Problems I've encountered:
WSL can't directly access USB devices by default. This is a problem for Arduino or hardware development.
Workaround: The usbipd-win project lets you forward USB to WSL.
Some VPNs (especially corporate ones) don't properly route WSL network traffic. Connecting to VPN can kill WSL's internet.
Workaround: Manually configure DNS or adjust VPN settings.
Windows and Linux have different permission models, so chmod can behave strangely in /mnt/c/.
Workaround: Keep projects inside WSL (~/) and there's no issue.
I've been using WSL 2 for over three years now. I own a MacBook, but my main development machine is still a Windows desktop with WSL. Why?
WSL 2 isn't just "a tool to run Linux on Windows." It's a technology that changed the development environment paradigm. It's remarkable that Microsoft decided to embrace Linux instead of fighting it, to build "a platform developers want to use."
I wish I could tell my 2015 self: "Wait five years. You'll be able to do amazing development on that Windows laptop."