article thumbnail
A guide to WSL
Windows Subsystem for Linux
12 min read
#

WSL — the Windows Subsystem for Linux — is Microsoft's answer to a question developers had been asking for decades: can I run real Linux on Windows without a virtual machine? The answer, as of WSL 2, is a resounding yes. If you spend any time developing on Windows, understanding WSL deeply will change how you work. This is that guide.


A Brief History

The original WSL (now called WSL 1) shipped in 2016 as part of Windows 10. It translated Linux system calls into Windows kernel calls — clever, but imperfect. Many programs that relied on low-level kernel behavior ran slowly or not at all.

WSL 2, released in 2020, took a fundamentally different approach: it ships a real Linux kernel inside a lightweight Hyper-V virtual machine. The VM boots in under two seconds, is managed entirely by Windows, and is essentially invisible during normal use. The result is near-native Linux performance, full system call compatibility, and support for tools like Docker that WSL 1 could never handle properly.

WSL 2 is what almost everyone should be using today.


Installation

On Windows 10 (build 19041 or later) and Windows 11, installation is a single command. Open PowerShell as Administrator:

wsl --install

This installs WSL 2, the Linux kernel, and Ubuntu as the default distribution. After a restart, Ubuntu will finish setup and ask you to create a Linux username and password (these are independent of your Windows credentials).

To see all available distributions and install a specific one:

# List distributions available in the Microsoft Store
wsl --list --online

# Install a specific distro
wsl --install -d Debian
wsl --install -d kali-linux
wsl --install -d Ubuntu-24.04

# See what's installed
wsl --list --verbose

The --verbose flag shows each distro's WSL version and whether it is currently running.


Daily Use: Getting Into Linux

Once installed, you have several ways to open a Linux terminal:

Windows Terminal is strongly recommended. It handles fonts, colors, tabs, and keyboard shortcuts far better than the legacy console host. Install it from the Microsoft Store if you do not already have it.

Inside your Linux shell, you have a full Ubuntu (or Debian, etc.) environment:

# Update packages
sudo apt update && sudo apt upgrade -y

# Install developer tools
sudo apt install git curl build-essential python3-pip

# Your Linux home directory
cd ~
pwd   # /home/yourusername

The File System: Two Worlds, One Machine

This is the part that trips people up most. WSL has two distinct file system locations, and understanding them matters for performance.

Linux files live under /home/yourusername/ inside WSL. This is the fast path — file operations here run at native Linux speeds. Keep your projects here.

Windows files are accessible from Linux under /mnt/c/, /mnt/d/, etc.:

# Access Windows Desktop from Linux
ls /mnt/c/Users/YourWindowsName/Desktop/

# Copy a file from Windows into Linux home
cp /mnt/c/Users/YourWindowsName/Downloads/data.csv ~/data.csv

Going the other direction, Linux files are accessible from Windows Explorer. Type \\wsl$ in the Explorer address bar and you will see all your installed distros as network shares. You can browse, copy, and open files from there.

The golden rule: store project files inside the Linux filesystem (~/projects/), not on /mnt/c/. Cross-filesystem operations (Linux tools reading Windows files) are significantly slower due to the translation layer. For disk-heavy work like large git repos or Node.js projects with deep node_modules trees, this difference is very noticeable.


Running Windows Commands From Linux (and Vice Versa)

WSL interop lets both worlds talk to each other seamlessly.

# Run any Windows executable from Linux (the .exe extension is required)
explorer.exe .          # open current Linux folder in Windows Explorer
notepad.exe readme.txt  # open a file in Notepad
code .                  # open VS Code in the current directory (no .exe needed for code)

# Open a file with its default Windows application
wslview image.png       # uses xdg-open under the hood, routes to Windows

From PowerShell, you can run Linux commands directly:

# Run a Linux command from PowerShell
wsl ls -la /home/yourusername

# Pipe between Windows and Linux tools
Get-Content data.txt | wsl grep "error"

# Run a Linux script
wsl bash ~/scripts/deploy.sh

This bidirectional interop is one of WSL's most underrated features. You can pipe output from PowerShell cmdlets into grep and awk, or use Linux tools to process files that live on Windows drives.


VS Code Integration

Visual Studio Code has first-class WSL support through the WSL extension. When you open a project from inside WSL with code ., VS Code launches a server inside Linux and connects to it from the Windows GUI. The result: your editor runs on Windows, but all the file access, terminals, linters, compilers, and language servers run natively inside Linux.

# From your WSL terminal
cd ~/projects/myapp
code .    # VS Code opens, connected to Linux

Extensions installed in this mode run inside Linux. This means Node.js, Python, Go, and Rust tools work exactly as they would on a native Linux machine. No path translation, no weird line ending issues, no "works on my machine" surprises when deploying to a Linux server.


Running Docker

Docker Desktop for Windows integrates with WSL 2 and uses it as its backend. Once Docker Desktop is installed and WSL integration is enabled (in Docker Desktop settings → Resources → WSL Integration), you can run Docker commands directly from your Linux shell:

docker run hello-world
docker compose up -d
docker ps

Containers run with near-native performance because Docker is running inside a real Linux kernel. This is a major improvement over the old Hyper-V-only approach. The Docker docs on WSL 2 backend cover the full setup.


Managing WSL Instances

# Shut down all running WSL instances
wsl --shutdown

# Shut down a specific distro
wsl --terminate Ubuntu

# Set the default distro (used when you type just "wsl")
wsl --set-default Ubuntu-24.04

# Export a distro to a file (great for backups)
wsl --export Ubuntu ubuntu-backup.tar

# Import it back (on the same machine or a new one)
wsl --import UbuntuRestore C:\WSL\UbuntuRestore ubuntu-backup.tar

# Unregister (delete) a distro — this deletes all data inside it
wsl --unregister Ubuntu

The export/import workflow is genuinely useful for moving a configured development environment to a new machine. Set up Linux exactly how you like it, export it, and import it anywhere.


Configuring WSL: wsl.conf and .wslconfig

Two files control WSL behavior.

/etc/wsl.conf (inside each Linux distro) controls per-distro settings:

[boot]
systemd=true          # Enable systemd (supported in WSL 2 since 2022)

[automount]
enabled = true
options = "metadata"  # Preserves Linux file permissions on Windows drives

[network]
hostname = mydevbox   # Custom hostname for this WSL instance

[interop]
appendWindowsPath = false  # Stops Windows PATH from leaking into Linux PATH

Setting appendWindowsPath = false is worth considering — the Windows PATH can slow down shell startup and pollute tool resolution in Linux.

%USERPROFILE%\.wslconfig (in Windows home directory) controls global WSL 2 settings:

[wsl2]
memory=8GB            # Cap RAM usage (default: 50% of system RAM)
processors=4          # Limit CPU cores
swap=2GB              # Swap file size
localhostForwarding=true

By default, WSL 2 can grow to consume up to half of your system's RAM. On a machine with 32 GB, that is 16 GB potentially going to the VM. Setting a reasonable cap prevents WSL from quietly eating resources while you work.


Enabling systemd

Since late 2022, WSL 2 supports systemd — the init system used by Ubuntu, Debian, and most modern Linux distros. Enable it in /etc/wsl.conf:

[boot]
systemd=true

Restart the distro (wsl --terminate Ubuntu then reopen it), and systemd is running. This unlocks service management:

sudo systemctl start postgresql
sudo systemctl enable nginx
sudo systemctl status redis
journalctl -u postgresql -f    # follow PostgreSQL logs

This makes WSL behave like a real Linux server for local development. Running Postgres, Redis, Nginx, or any other service under systemd inside WSL means they start automatically when WSL boots and can be managed with standard Linux tooling.


Tips for a Better WSL Experience

Use Zsh or Fish instead of Bash. Both install normally via apt:

sudo apt install zsh
chsh -s $(which zsh)

Oh My Zsh adds autocompletion, plugins, and themes in one installer command.

Set up SSH keys inside Linux, not on Windows. Keys stored in ~/.ssh/ inside WSL work with git, remote servers, and scp exactly as they would on a native Linux machine.

Use wsl-open (or the built-in wslview) to open files and URLs from the Linux terminal in their default Windows applications. Saves constant switching.

Mount additional drives by adding them to /etc/fstab or using wsl --mount for physical disks.


Where to Go From Here

The official WSL documentation at Microsoft Learn is thorough and kept up to date. The WSL GitHub repository is where bugs are tracked and new features are announced.

Craig Loewen at Microsoft is the product manager for WSL and posts frequently about new features. The r/bashonubuntuonwindows subreddit (yes, the name is historical) is an active community for WSL questions and tips.


WSL 2 has made Windows a genuinely comfortable place to do Linux development. The old workarounds — dual booting, full VMs, Cygwin, Git Bash — are largely unnecessary now. With a real kernel, Docker support, VS Code integration, and systemd, WSL gives you most of what a dedicated Linux machine would, without leaving your Windows environment behind. The two-second boot time alone is enough reason to give it a serious look.