I often get asked by friends and workmates how my WSL setup, on my Machines for both, personal and professional work, looks like
and how I even use zsh as my native shell for Windows tasks.

Every time this happens, I link them to an old post of mine, accompanied by a giant wall of text since a lot has happened since then.
Since I recently (at the time of writing) changed the company I work for and Microsoft released Win11, now is a better time than ever to finally post again about my WSL setup (and maybe even keep it up to date?)


for everyone that doesn't feel like reading my gibberish:



Before starting with setting up WSL and the shell environment, I'd recommend getting a proper Terminal emulator.

Choices are not as limited anymore as they were a few years ago.
The "Windows Terminal" has a pretty good feature set that should be enough for regular use.
Best of all: It's fast!
It even got quake-mode not too long ago, and can replace the pesky default "conhost.exe".
It has become my go-to and only terminal emulator on Windows.

However, it lacks a few customizations options like auto-start in hidden-quake mode, and always on top separately for quake-mode and normal-mode instances.

If you need that, you can use ConEmu as it is still the most fully-featured Terminal emulator for Windows.
Be aware that it can be kinda slow, though.


Next up, get proper fonts with a high number of glyphs (icons), like "nerd-fonts".
As there is no official all-in-one installer for Windows you can use your favorite Windows package-manager, or just opt to install a handful of select fonts as I did.

Windows Sudo

(Trust me, it's useful!)
I'd also strongly recommend you install gsudo, basically sudo for Windows.
If you're on Win11 you can just winget install gsudo in an elevated PowerShell.
After the installing, you can elevate any Windows process/command by just prefixing gsudo


I personally came to love Arch Linux and I think that its minimalist approach fits very well for being used in the WSL.  
Although Arch is not officially supported, you can use one of many distro-launchers/-installers to get it going.
I personally use ArchWSL as it's well maintained and I've never had much issue running it, even professionally.

For a basic installation, you can refer to their excellent guide here

If you've never used WSL before or start with a fresh Windows install, use Microsoft's guide to install WSL, but install ArchWSL from their GitHub page, instead of any other distro from the Microsoft store.

With any recent version of WSL2, pretty much the only out-of-the-box issue is missing systemd.
This might not be that big of a deal when running the default Ubuntu flavor, but on Arch, it can be really painful.
Thankfully the community has got our back and there are several alternatives you can run.
With them one can even use docker, without the need to install docker-for-windows.

I personally use genie as it breaks the least of my stuff and is easy to set up and configure.
Currently, only QT-based GUI programs don't work properly when started from a shell inside the genie-bottle.

At the time of writing, if you want wslg GUI support, you will need to enable the wslg-xwayland.socket systemd units manually:

systemctl enable wslg-xwayland.socket

I've also decided to edit the default config of genie (/etc/genie.ini) and clone the windows-path, like wsl does without it, to provide a more seamless experience.


File system gotchas

On recent Windows versions, File-explorer already provides access to Linux files and tries to provide a seamless experience.
However it can lead to compatibility issues with some programs, so I think it's still best to just map the WSL files as a network drive.
If you use \\wsl.localhost\ as path for your network-drive, explorer will just let you do that.

When coming from the Linux-side, it's possible to project file-permissions and additional metadata to NTFS, but it's still not enabled by default 🤦‍♂️
When enabled, this will allow you, for example, to symlink your ssh-key-folder generated on Windows, eliminating the need to set up multiple keys for the same damn machine.

To get that working, you need to edit the automount options in /etc/wsl.conf
Or you can just copy&pasta my config:

# Enable extra metadata options by default
enabled = true
root = /mnt/
options = "metadata,rw,umask=22,fmask=11"
mountFsTab = false

# Enable DNS  even though these are turned on by default, we’ll specify here just to be explicit.
generateHosts = true
generateResolvConf = true



Another feature that is missing from WSL out of the box is support for USB devices.
However, recently (at time of writing) M$ did put some work into it, and posted about a way to make it happen.

That's great news for anyone who's into Arduino or similar stuff or even car-hacking, as I personally find that that kinda stuff is just easier to set up on Linux than on Windows.

Basically, just install usbipd-win...

winget install --interactive --exact dorssel.usbipd-win

...and if you're using Arch too, nothing more is required (required Ubuntu packages can be taken from M$s blog post)

In any elevated command-prompt you can now list compatible USB-devices and attach/detach them to/from WSL using usbipd wsl list and usbipd wsl attach --busid XX
If you've installed the aforementioned gsudo you can also just run the commans directly from WSL like this (which I prefer)

cmd.exe /c gsudo usbipd wsl list
cmd.exe /c gsudo usbipd wsl attach --busid 4-4


Yet another tool I very strongly recommend is wsl-open.
It allows you to use Linux's standart xdg-open command for files and links, and they magically open with the corresponding native Windows application.
It really helps to get a more seamless experience, going so far that I use zsh in the WSL as my regular windows shell.
You can just install it via npm

sudo npm install -g wsl-open

But there is also a non-npm installation described on their GitHub page, if you're not into node.

Port Fowards

Normally, all network ports are forwarded to the Windows host, so that you can access webservers/databases and whatnot from Windows.
I say normally, as it sometimes breaks for no reason(???)
What it doesn't allow you to do is make network services available from outside your machine.

Total bummer if you want ssh-access to your machine, or just quickly share a few files to a colleague via some quickly spawned up webserver.

Luckily again, the community has got our backs again and there is a PowerShell script that allows one to forward any ports directly to WSL.
Here's a version that works with recent Arch versions in WSL, also found on GitHub

# WSL2 network port forwarding script v1
#   for enable script, 'Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope CurrentUser' in Powershell,
#   for delete exist rules and ports use 'delete' as parameter, for show ports use 'list' as parameter.
#   written by Daehyuk Ahn, Aug-1-2020

# Display all portproxy information
If ($Args[0] -eq "list") {
    netsh interface portproxy show v4tov4;

# If elevation needed, start new process
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
  # Relaunch as an elevated process:
  Start-Process powershell.exe "-File",('"{0}"' -f $MyInvocation.MyCommand.Path),"$Args runas" -Verb RunAs

# You should modify '$Ports' for your applications
$Ports = (667)

# Check WSL ip address
#wsl /bin/sh -c "ip route get 1 | grep 1. | awk '{print \`$3}'" | Set-Variable -Name "WSL"
#wsl hostname -i | Set-Variable -Name "WSL"
$WSL = wsl bash -c "ifconfig eth0 | grep 'inet ' | awk '{print \`$2}'"
echo $WSL
$found = $WSL -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
if (-not $found) {
  echo "WSL2 cannot be found. Terminate script.";

# Remove and Create NetFireWallRule
Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock';
if ($Args[0] -ne "delete") {
  New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $Ports -Action Allow -Protocol TCP;
  New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $Ports -Action Allow -Protocol TCP;

# Add each port into portproxy
$Addr = ""
Foreach ($Port in $Ports) {
    iex "netsh interface portproxy delete v4tov4 listenaddress=$Addr listenport=$Port | Out-Null";
    if ($Args[0] -ne "delete") {
        iex "netsh interface portproxy add v4tov4 listenaddress=$Addr listenport=$Port connectaddress=$WSL connectport=$Port | Out-Null";

# Display all portproxy information
netsh interface portproxy show v4tov4;

# Give user to chance to see above list when relaunched start
If ($Args[0] -eq "runas" -Or $Args[1] -eq "runas") {
  Write-Host -NoNewLine 'Press any key to close! ';
  $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');

One thing to note, though, is that you cannot run your ssh-server on the default port 22. If you do, the native Windows ssh-server will just get the connection, instead of the one on your WSL.

Custom Kernel

Most people will not care about running a custom Kernel on WSL, however, if you need drivers that are not included by default, like SocketCan for when you're into car hacking there is no way around it.
Here are the basic instruction to get a custom Kernel up and running:

#get kernel version
uname -r

# get matching version, this is mine
wget https://github.com/microsoft/WSL2-Linux-Kernel/archive/refs/tags/linux-msft-wsl-

#extract it (change to your kernel version)
tar -xf linux-msft-wsl-

# build linux/modules

# change to the folder tar extracted too, yours might be different
cd  WSL2-Linux-Kernel-linux-msft-wsl-
cat /proc/config.gz | gunzip > .config
make prepare modules_prepare -j $(expr $(nproc) - 1)

# select your drivers/modules here
make menuconfig -j $(expr $(nproc) - 1)
make modules -j $(expr $(nproc) - 1)
sudo make modules_install
make -j $(expr $(nproc) - 1)
sudo make install

# copy it to windows drive, change <yourwindowsloginname> to your windows users 
cp vmlinux /mnt/c/Users/<yourwindowsloginname>/

#create this file
vim /mnt/c/Users/<yourwindowsloginname>/.wslconfig

#with these contents

# exit wsl
wsl.exe --shutdown