The ULTIMATE setup for (Web)Developers with WSL2 and Docker – Part 1

This is going to be a multipart-series of how to setup the ULTIMATE WSL2 and Docker environment, to get the most out of it, during your everyday developer’s life.
Now, let’s first tackle the meaning of ULTIMATE, since everyone has it’s own interpretation on what it means. S
- Reliable – The setup has to be bulletproof, in case of system failures or data loss, and it has to be organized, so that everything is on it’s proper place.
- Fast – Working on projects, especially web projects, where you may have to reload a site multiple times to see changes, has to perform like a bare-metal setup
- Convenient – There’s nothing more frustrating than having to turn knobs across multiple places, to get the most basic things to work.
Now that we’re on the same page about the meaning of ULTIMATE, let’s get started!
The problem with the initial WSL2 setup
Let’s start this series off with the proper setup of WSL2.
I assume that you already have a successfully running WSL2 distro on your Windows machine. If you installed the distro from the Microsoft Store, it’s probably located somewhere in your AppData
folder, God knows where.
Also, you may be working on your fancy NodeJS server, which is probably located under /mnt/c/Users/<username>/Projects/NodeJS/my-most-paying-client
or even /mnt/d
if you’re on the secure side and have your sensitive data NOT stored on C:\
.
So, there are two problems here.
First, remember when I said it has to be reliable. Well, that’s absolutely not the case here. Since Windows can crash, and your AppData
folder cannot be moved outside of C:\Users\username
, you’re most likely to lose all your hard-efforts you put into setting up your distro.
The second issue is speed. As you probably may know (or may not know): to get the best performance out of WSL2 (which is near bare-metal) you MUST store your project-files inside WSL2, like /home/<username>
.
The ULTIMATE solution
Fortunately we can tackle both of these problems with some setup.
Recently, WSL has introduced a nice feature --mount
, which allows you to mount ext4 disk partitions into WSL and access them natively like you would do with any other ext4 partition. This also comes with a huge performance benefit, eliminating the need of the slow P9 filesystem.
- Create an empty vhd-file and format it as ext4
- Mount the vhd-file into WSL as a partition
- Move all projects into that partition
- Symlink the partition as our /home/<username> folder
1. Creating the virtual hard disk (VHD)
First you need to create a virtual disk. You can easily do that with Windows Disk Management. Make sure to create a dynamically expanding disk, which will slowly grow in size as you copy files to it.
If you’re a power-user, you probably have a separate partition for your projects, images, etc. Make sure to save the newly created virtual disk somewhere save, in case of a system crash. Also make sure to include it into your backup routine.

2. Formatting VHD to ext4
There are many ways on how to format a disk to ext4 under Windows. The easiest I’ve found so far, is AOMEI Partition Assistant which comes with a free trial, and is enough for this purpose (not sponsored).
Simply open up AOMEI Partition Assistant and create an ext4 partition on your newly created and mounted disk.

3. Mounting VHD into WSL2
$number = $((Mount-VHD -Path D:\path_to_virtual_disk.vhdx -PassThru | Get-Disk).Number)
$diskDrive = "\\.\PHYSICALDRIVE$number"
wsl --mount $diskDrive -p 1
# remove previously created symlink
wsl rm -rf /mnt/wsl/vhd_data
# add new symlink
wsl ln -s /mnt/wsl/$diskDrive"p1" /mnt/wsl/vhd_data
# Remember this line for our next part
# Start-Process "C:\Program Files\Docker\Docker\Docker Desktop.exe"
Now you should be able to access the disk under /mnt/wsl/dataDisk
and see a lost+found
folder in there.
You may also want to save this script somewhere on your computer and run it with Task Scheduler every time you login into your PC.
Simply add this line into the action of Start a program
:
-ExecutionPolicy Bypass -File D:\path_to\vhd.ps1
4. Migrating user data to VHD
Let’s create our user folder there, move all our /home/<username>/*
files into the new folder and symlink it.
cd /mnt/wsl/vhd_data
sudo mkdir <username>
sudo chown <username>:<username> username
sudo mv /home/<username> /home/<username>_bak
sudo ln -s /mnt/wsl/vhd_data/<username> /home/<username>
mv /home/<username>_bak/* /home/<username>/
The ultimate bonus
Now that you have migrated your user data to the VHD, let’s tackle the convenient part of the ULTIMATE setup. This allows you to switch your Linux distros as you wish, while keeping all your data in one central place.
You want to run Kali alongside Ubuntu while still being able to access your user data? This comes in handy, if sometimes things don’t work as expected and you want to make sure if the problem is in Ubuntu or elsewhere. Just make sure to have the same username as you have on Ubuntu and execute these commands.
# assuming your distro installation is freshly setup
sudo rm -rf /home/<username>
sudo ln -s /mnt/wsl/PPHYSICALDRIVE<number>p1/<username> /home/<username>
I hope you learned something, and stay tuned for the next part, where I’ll cover the proper setup of Docker inside WSL2.
Part 2
Continue to the second part:
https://dnmc.in/2021/02/26/the-ultimate-setup-for-webdevelopers-with-wsl2-and-docker-part-2/
[…] https://dnmc.in/2021/02/01/the-ultimate-setup-for-webdevelopers-with-wsl2-and-docker-part-1 […]
I’ve really enjoyed and loved this setup for my Arch WSL, but I have a persistent problem – whenever I log out or let my computer run to sleep, I cannot unmount the drive nor run vhd.ps1 again until I restart my computer. I recall seeing something somewhere about windows sleep being screwy with WSL mounts, but I can’t find it again. Do you have this same issue?
Unfortunately, I’m not familiar with Arch since I use Ubuntu. But currently I mount the vhdx file at boot. After that, when I wake my PC from sleep (no matter for how long, be it a few minutes, or the next day) the vhdx file is still mounted and working properly inside WSL2.
wsl –mount is only available in preview edition.
Why are you using a feature only available in a preview edition particularly without noting that this only is allowed under the preview edition ? The current build is 20H2
https://docs.microsoft.com/en-us/windows/wsl/wsl2-mount-disk
https://docs.microsoft.com/en-us/windows/release-health/release-information
Note that if you are using the 20H2 current Windows 10 build, you can guestmount a vhd drive, see:
https://blog.codybunch.com/2020/10/16/WSL2-Mount-vhdx-to-WSL2/
I have done that but don’t know if that would work for your purposes.
Sorry, my bad! Since my articles are targeted towards power-users, I assume that everyone is on the preview-channel. How’s the read/write performance of `guestmount`? Since it accesses `/mnt/c/` from within WSL2, which is usually very bad.
I had some issues with vhd.ps1, so I made some edits that should work. Mount-VHD cmdlet wasn’t available to me since I don’t use Hyper-V, but Mount-DiskImage does. I created another variable $bashDiskDrive to format the name to be as WSL mounts it.
“`powershell
$number = $((Mount-DiskImage -ImagePath “D:DataVirtualBox VMsWSL2 VHDwsl2.vhdx” -PassThru | Get-Disk).Number)
$diskDrive = “\.PHYSICALDRIVE$number”
$bashDiskDrive = “PHYSICALDRIVE$number” + “p1”
wsl –mount $diskDrive -p 1
# remove previously created symlink
wsl rm -rf /mnt/wsl/vhd_data
# add new symlink
wsl ln -s /mnt/wsl/$bashDiskDrive /mnt/wsl/vhd_data
# Remember this line for our next part
# Start-Process “C:Program FilesDockerDockerDocker Desktop.exe”
“`
Formatting borked. Link here: https://gist.github.com/igoforth/5d6315489f4401dc380fe306a525a998