DEV Community

Miro
Miro

Posted on

Manually installing WSL2 distributions

There are a few different ways of installing Linux distributions for WSL. The easiest, using Microsoft Store, or using tools like LxRunOffline or wsldl. But distribution can be installed manually as well.

WSL command line allows to export distribution to a tar file, or to import from a tar file as a new distribution.

Let's say I have Ubuntu installed from Microsoft Store, if I list them from a command line I will get something like this:

PS E:\Temp> wsl --list --all -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Stopped         2

And if I export it:

PS E:\Temp> wsl --export Ubuntu-20.04 E:\WSL2\ubuntu.tar

I will get a tar file containing a root filesystem of my Ubuntu distribution. And I can import that as a new distribution:

PS E:\Temp> wsl --import Ubuntu-Copy E:\WSL2\Ubuntu-Copy E:\WSL2\ubuntu.tar

This will create a folder Ubuntu-Copy folder in the E:\WSL2 and the ext4.vhdx file in that folder. Virtual hard disk is an ext4 partitioned disk containing the contents of the tar file. Listing distributions now shows two of them.

PS E:\Temp> wsl --list --all -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Stopped         2
  Ubuntu-Copy     Stopped         2

If I start the second distribution, I will be logged in as a root account:

PS E:\Temp> wsl -d Ubuntu-Copy
root@PC123:/mnt/e/Temp#

Ok, so if import works for the exported root filesystem, will it work for any root filesystem? And the answer is Yes!

For example, if I download alpine-minirootfs-3.12.0-x86_64.tar.gz from https://alpinelinux.org/downloads/ I can import it like this:

PS E:\Temp> wsl --import Alpine-3.12 E:\WSL2\Alpine-3.12 E:\WSL2\alpine-minirootfs-3.12.0-x86_64.tar.gz

It even works with tar.gz files, not just .tar files. In fact, Alpine distribution available in Microsoft Store does exactly that. Downloads this file and installs it. Source for the launcher is available here: https://github.com/agowa338/WSL-DistroLauncher-Alpine

Similar to the reference implementation of the launcher (WSL-DistroLauncher) it allows user creation on start-up and it has some additional fixes. See the DistroSpecial.h file.

After distribution is complete I can manually create user, the same way launcher does

PS E:\Temp> wsl -d Alpine-3.12 /usr/sbin/adduser -g '' -D user1
Changing password for user1
New password:
Retype password:
passwd: password for user1 changed by root

And then logging in like

PS E:\Temp> wsl -d Alpine-3.12 -u user1
PC123:/mnt/e/Temp$

In a nutshell this is it. A new distribution is manually installed.

Important thing to note:
By unregistering the distribution like wsl --unregister Ubuntu-20.04 it will delete the associated ext4.vhdx file. Make sure to backup any important data form the virtual hard disk before unregistering.

Start-up scripts

Instead manually creating users and setting everything up, I can write all commands in a text file and execute them.

For example, a dummy init file that displays current date and release name

date
cat /etc/*-release

Start like

E:\Temp> wsl -d Debian-10-Slim < init

And the result is

Fri Aug  6 23:08:29 BST 2020
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

Or run that file directly as a shell script from a know location

PS E:\Temp> wsl -d Alpine-3.12 /bin/sh /mnt/e/Temp/init

Fri Aug  6 22:17:05 UTC 2020
3.12.0
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.12.0
PRETTY_NAME="Alpine Linux v3.12"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"

Registry keys

When distribution is installed its settings are stored in the registry HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{GUID}

Guid is, as far as I can tell, randomly generated for every distribution installation

For example, official installation of Ubuntu creates similar to the following:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{2506e37c-8479-4433-9ea8-7549090aa5bf}]
"State"=dword:00000001
"DistributionName"="Ubuntu-20.04"
"Version"=dword:00000002
"BasePath"="C:\\Users\\User\\AppData\\Local\\Packages\\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\\LocalState"
"Flags"=dword:0000000f
"DefaultUid"=dword:000003e8
"PackageFamilyName"="CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc"
"KernelCommandLine"="BOOT_IMAGE=/kernel init=/init"
"DefaultEnvironment"=hex(7):<snipped>

Which means that virtual hard disk is stored in the C:\Users\User\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\LocalState folder and default Uid is 1000 (0x3e8).

Looking at the Alpine distribution:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{b9a51057-c168-4798-a89e-483c97278f3c}]
"State"=dword:00000001
"DistributionName"="Alpine-3.12"
"Version"=dword:00000002
"BasePath"="\\\\?\\E:\\WSL2\\Alpine-3.12"
"Flags"=dword:0000000f
"DefaultUid"=dword:00000000

it omits some settings, but BasePath points to a folder containing ext4.vhdx file and DefaultUid is the Uid of the user when starting the distribution without the -u <user> option.

Moving a distribution to a different location requires just to move the ext4.vhdx to a new folder and update the BasePath value. Similarly, changing a default user requires an update to the DefaultUid value.

To "restore" a missing distribution, copy a registry key, update the DistributionName, BasePath, and DefaultUid values, and it is ready to go. Or import an empty tar file (created like tar -zvcf empty.tar.gz -T /dev/null) and swap out the resulting empty ext4.vhdx with the real one.

Other distributions

Following the examples from the wsldl project, a simple way of installing different linux distributions is to take root filesystem used to make docker images. For example Dockerfile for Debian Buster Slim says ADD rootfs.tar.xz /. I can download the rootfs.tar.xz, unpack it since WSL doesn't recognize tar.xz file format, import it and run it like:

PS E:\Temp> wsl --import Debian-10-Slim E:\WSL2\Debian-10-Slim E:\WSL2\debian-buster-slim-20200803-rootfs.tar
PS E:\Temp> wsl -d Debian-10-Slim
root@PC123:/mnt/e/Temp#

Official install.tar.gz

When installing Ubuntu or Debian Linux distribution from the Microsoft Store root filesystem is downloaded as a part of Store App installation. This means that initial filesystem tar, install.tar.gz can be found in the C:\Program Files\WindowsApps \CanonicalGroupLimited.Ubuntu20.04onWindows_2004.2020.424.0_x64__79rhkp1fndgsc\ folder, for the Ubuntu 20.04 installation.

For a different version, folder name in the C:\Program Files\WindowsApps will be different, but it should contain install.tar.gz file.

And that file can be imported as a new distribution in the same way as mentioned above.

Differencing disks

Since WSL2 is using vhdx disks, I was wondering can I use differencing disks. And the answer is again Yes.

Having 5 instances of Ubuntu will take about 5GB of space immediately. Base vhdx is about 1GB. But, by leveraging differencing disks, I can have one base disk, and 5 smaller, differencing disks.

First stop all distributions with wsl --shutdown, then move existing ext4.vhdx to a new folder (one level up in my case), and create a differencing disk with the ext4.vhdx name.

PS E:\WSL2\Ubuntu-20.04> New-VHD -ParentPath ..\base.vhdx -Path ext4.vhdx -Differencing -BlockSizeBytes 1048576

When creating virtual hard disks for linux, make sure that you use 1MB block size. Default block size of 32MB will result in fairly large vhdx file once it starts to get filled.

Only article where I found this recommendation is Understanding and Working with VHD(X) Files

For Linux systems, there is a soft recommendation to use a 1 megabyte block size

By examining ext4.vhdx created by the installation, details also show 1MB block size

PS E:\WSL2\Ubuntu-Copy> Get-VHD .\ext4.vhdx | Format-List

ComputerName            : PC123
Path                    : E:\WSL2\Ubuntu-Copy\ext4.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 1164967936
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 1048576
ParentPath              :
DiskIdentifier          : A96ADE57-4838-48EA-97A9-DE02790ADCB4
FragmentationPercentage : 53
Alignment               : 1
Attached                : False
DiskNumber              :
IsPMEMCompatible        : False
AddressAbstractionType  : None
Number                  :

BlockSize effect on a dynamic disk file size

A little digression. Comparing a file size for vhdx with 1MB block size vs default 32MB block size.

I've created two vhdx files:

PS E:\Temp> New-VHD -Path .\256_default.vhdx -SizeBytes 256GB

Path                    : E:\Temp\256_default.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 4194304
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 33554432

and

PS E:\Temp> New-VHD -Path .\256_1mb.vhdx -SizeBytes 256GB -BlockSizeBytes 1MB

Path                    : E:\Temp\256_1mb.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 6291456
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 1048576

Initial sizes were 4MB and 6MB.

Attached them to a Hyper-V VM, booted the alpine-virt-3.12.0-x86_64.iso .

Then I ran mkfs.ext4 /dev/sda and mkfs.ext4 /dev/sdb and shut down the VM.

Resulting files are

PS E:\Temp> Get-VHD .\256_default.vhdx | Format-List

Path                    : E:\Temp\256_default.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 4869586944
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 33554432

and

PS E:\Temp> Get-VHD .\256_1mb.vhdx | Format-List

Path                    : E:\Temp\256_1mb.vhdx
VhdFormat               : VHDX
VhdType                 : Dynamic
FileSize                : 162529280
Size                    : 274877906944
MinimumSize             :
LogicalSectorSize       : 512
PhysicalSectorSize      : 4096
BlockSize               : 1048576

As you can see, after making the ext4 filesystem on a 256GB dynamic disk, the one with a 32MB block size grew to 4,53GB while while the one with the 1MB block size grew to just 155MB.

The end

Anyway, WSL2 seems interesting enough 😁

Top comments (2)

Collapse
 
silopolis profile image
Jérémie Tarot

That is one hell of a good post !
Surely putting WSL distro management on steroid :)

Thanks for sharing

Collapse
 
tanukus profile image
tanukus

Nice post!