WSL: Let's Create Multiple Instances with Shared Home Directory
Tags: windows, linux, wsl, shared
Published: 2023-10-27 16:36:38

> ℹ️ The article has been re-edited, keeping its content unchanged while enhancing it where possible and making it clearer where needed.

## Table of Contents
- [Intro](#intro)
- [Prerequisites](#prerequisites)
- [Preparing the virtual disk for your home](#prepare-vhdx)
- [Mounting the virtual disk on WSL](#mount-vhdx)
- [Preparing the Linux distribution](#prepare-distro)
- [Creating a new session](#create-session)
- [Configuring the new session](#configure-session)
- [Initializing the home virtual disk](#init-vhdx)
- [Mounting the virtual disk to the home directory](#mount-home)
- [Ready, Set, Go 🚀](#ready-set-go)
- [Troubleshooting](#troubleshooting)
- [References](#refs)

## Intro <a name="intro"></a>
I often find myself fighting with the problem of having more than one WSL session for the same Linux distribution.

> 💡It is not officially possible to install two or more fresh copies of the same distro unless you rename the executable downloaded from the Microsoft Store for each session you want to install.

Of course, it is possible to export a previously installed session and import it as many times as needed, but this leads to another (in my humble opinion) problem – the duplication of your home directory for every imported session. Having more independent sessions, on the other hand, introduces the challenge of mirroring your home directory between each session, thereby necessitating a shared home directory.

To address this issue, I conducted research on the internet to find a solution that worked for me. I then documented it in a step-by-step guide (or more like a memo for myself), putting it in writing for future reference.

**DISCLAIMER: The following steps require running Windows 10/11 and performing actions that demand Administrator privileges. Incorrect execution can lead to disastrous results. While these steps have proven successful on my system, there is no guarantee they will work on yours. Proceed only if you are confident in your understanding of the process, and I cannot be held responsible if any issues arise.**

## Prerequisites <a name="prerequisites"></a>
To follow this guide, it is expected that the system has the following:

- Windows 10/11 Education/Pro/Enterprise (Home edition is supported but may require additional steps)
- Windows Subsystem for Linux version 2 installed
- Sufficient disk space for sessions' and home's virtual disks
- Administrator privileges
- One or more Linux rootfs tarballs
- Basic understanding of shell commands

## Preparing the virtual disk for your home <a name="prepare-vhdx"></a>
> ⚠️ This step is required **ONLY** if you haven't already created your own virtual disk for the home directory.

The initial step is to create a virtual disk serving as our shared home partition across all sessions. Various methods can achieve this.

{% details Option 1: Disk Management snap-in %}
It can be created via the **Disk Management** snap-in (`diskmgmt.msc`) by following these steps:

- Select **Create VHD** from the **Action** menu.
- Set options for **VHDX** and **Dynamically expanding**.
- Specify the path and size for the virtual disk file.
{% enddetails %}

{% details Option 2: Windows Settings %}
The same result can be achieved through the **Windows Settings** panel by selecting **Create VHD** in **Disks & volumes** under **Settings > System > Storage > Advanced storage settings**.
{% enddetails %}

{% details Option 3: Diskpart %}
> ℹ️ To perform these steps, it is necessary to run the console with Administrator privileges.

The same result can be achieved via the command line using **diskpart** (`diskpart.exe`). Once the console (`cmd.exe`) is opened, simply type:
```shell
diskpart.exe

DISKPART> create vdisk file="<path>" maximum=<size> type=expandable
```
where `<path>` is the file path for the virtual disk, and `<size>` represents the maximum size (in KiB) to be assigned to the virtual disk.
{% enddetails %}

{% details Option 4: Hyper-V %}
> ℹ️ Running PowerShell with Administrator privileges is necessary. Users of Windows Home edition need to complete an [additional step](#troubleshooting) before being able to use the Hyper-V module.

In PowerShell it is possible to use the `New-VHD` cmdlet from the `Hyper-V` module:
```powershell
New-VHD -Path <path> -Dynamic -SizeBytes <size>
```
where `<path>` is the file path to the virtual disk, and `<size>` represents the maximum size, specified in MiB, GiB, or TiB, to be assigned to it.
{% enddetails %}

> ⚠️ Ensure that you have sufficient space for the virtual disk, as its minimum size will significantly increase once formatted in `ext4`. To estimate the required space, you can use the following formula.

{% katex %}
\text{Reserved Space} = \text{Partition Size} \times \left(\frac{\text{Reserved ext4 Percentage}}{100}\right)\newline
{% endkatex %}

As example, for a 10GiB virtual disk formatted in ext4:

{% katex %}
10737418240 \times \left(\frac{5}{100}\right) = 536870912\text{ (\textasciitilde512MiB)}
{% endkatex %}

## Mounting the virtual disk on WSL <a name="mount-vhdx"></a>
After creating the virtual disk, it is necessary to make it available in WSL for use by all sessions. In PowerShell type:
```powershell
wsl.exe --mount <virtual_disk_path> --vhd --bare 
```
This will make the virtual disk at `<virtual_disk_path>` available in the current WSL, ready for mounting in every session."

> ℹ️ This step is required each time a new virtual disk is created and after every system restart, as there is currently no way to automount the disks when WSL is initiated.

## Preparing the Linux distribution <a name="prepare-distro"></a>
The guide should work with any Linux distribution that offers a rootfs tarball compatible with WSL.

For the sake of simplicity, all steps will be executed in PowerShell with Administrator privileges.

{% details Ubuntu %}
You can download an image from [https://cloud-images.ubuntu.com/releases/](https://cloud-images.ubuntu.com/releases/) (for releases before 22.04) or [https://cloud-images.ubuntu.com/wsl/](https://cloud-images.ubuntu.com/wsl/) (for releases from 22.04 and later).
```powershell
Invoke-WebRequest https://cloud-images.ubuntu.com/wsl/mantic/current/ubuntu-mantic-wsl-amd64-wsl.rootfs.tar.gz -OutFile ubuntu-mantic-wsl-amd64-wsl.rootfs.tar.gz
```
{% enddetails %}

{% details Arch %}
**Additional steps are required within a Linux session to prepare Arch Linux before importing**. Firstly, download the **bootstrap** archive from the Arch repository [https://archmirror.it/repos/iso/](https://archmirror.it/repos/iso/):
```bash
wget https://geo.mirror.pkgbuild.com/iso/latest/archlinux-bootstrap-x86_64.tar.gz
```
or
```bash
wget https://geo.mirror.pkgbuild.com/iso/latest/archlinux-bootstrap-x86_64.tar.zstd
```
After downloading, it is necessary to prepare the **rootfs** tarball required for the import in WSL.
```bash
sudo tar -xzf archlinux-bootstrap-x86_64.tar.gz
# or, if tar ball is compressed with zstd
sudo tar --zstd -xf archlinux-bootstrap-x86_64.tar.std

cd root.x86_64

sudo tar -czvf ArchWSL.tar.gz .

sudo cp ArchWSL.tar.gz /mnt/<path_for_tarball>
```
where `<path_for_tarball>` represents the location on your Windows system where the tarball will be stored for future imports.
{% enddetails %}

## Creating a new session <a name="create-session"></a>
To create a new Linux session in WSL, the first step is to import the chosen rootfs tarball. In PowerShell, type
```powershell
wsl.exe --import <session> <session_path> <rootfs_tarball_path> --version 2
```
where `<session>` is the name to assign to the newly created Linux session, `<session_path>` is the directory where the session's virtual disk will be created by WSL (the virtual disk always has the name `ext4.vhdx`, so it is mandatory to specify a different directory for each unique instance), and `<rootfs_tarball_path>` is the path to the tarball downloaded in the previous step.

Then, start the session to allow WSL to install the distribution:
```powershell
wsl.exe -d <session>
```
At this point, the bare minimum Linux session is ready. However, unlike the default installation procedure via `wsl --install`, which installs a session with Ubuntu and an already configured home directory, the session just created only has the root user. Further configuration is required to connect an user home directory to the previously created home virtual disk.

## Configuring the new session <a name="configure-session"></a>
WSL sessions can be configured using the `wsl.conf` configuration file. This is the first step required to configure the newly created session. Open the file with your preferred editor (here, `nano` is used for the sake of simplicity).
```bash
nano /etc/wsl.conf
```
Type in the following, save, and then exit:
```ini
[boot]
systemd=true

[user]
default=<user_name>
```
where `<user_name>` is the default non-root login user when the session starts.

> 💡Further information about the configuration parameters available for the `wsl.conf` file can be found in the [documentation](https://learn.microsoft.com/en-us/windows/wsl/wsl-config#wslconf) on the Microsoft website.

Now, create the user and add it to the sudoers group.

{% details Ubuntu %}
```shell
USERNAME=<user_name> adduser $USERNAME; passwd $USERNAME; adduser $USERNAME sudo; mkdir /home/$USERNAME; chown $USERNAME: /home/$USERNAME
```
{% enddetails %}

{% details Arch %}
```bash
USERNAME=<user_name> useradd $USERNAME; passwd $USERNAME; mkdir /home/$USERNAME; chown $USERNAME: /home/$USERNAME
```
In Arch, you can create a file in `/etc/sudoers.d/` with the following data:
```ini
<user_name> ALL=(ALL:ALL) ALL
```
Since Arch does not come configured as Ubuntu does, it is necessary to initialize and update Pacman:
```bash
curl -s "https://gitlab.archlinux.org/pacman/pacman-contrib/-/raw/master/src/rankmirrors.sh.in" > rankmirrors.sh

chmod +x rankmirrors.sh

COUNTRY=US curl -s "https://www.archlinux.org/mirrorlist/?country=${COUNTRY}&protocol=https&ip_version=4" | cut -b 2- | ./rankmirrors.sh -n 10 - > /etc/pacman.d/mirrorlist

pacman -Syu
```
And setup the locale:
```bash
echo "en_US.UTF-8 UTF-8" > /etc/locale.gen

locale-gen
```
{% enddetails %}

## Initializing the home virtual disk <a name="init-vhdx"></a>
> ⚠️ These steps are required **ONLY** if the virtual disk has not yet been initialized and formatted. **DO NOT PERFORM THESE STEPS ON AN EXISTING VIRTUAL DISK, OR YOU WILL LOSE ALL THE DATA ON IT.**

The first thing to do is to search for the device mounted in WSL:
```bash
lsblk

NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT  
...  
...  
sdc    8:48   0 10G   0 disk  
...
```
In the example above, the device is `sdc` with a size of `10G`. You will need to identify the device for the virtual disk you prepared as your home.

Once found, it must be initialized by creating the partition table using the `parted` command. You can install it on Ubuntu with `apt install parted`, or on Arch with `pacman -S parted`.
```bash
parted /dev/sdc mklabel msdos
parted -a optimal /dev/sdc mkpart primary ext4 0% 100%
```
Here, replace `/dev/sdc` with the specific device you previously found.

If everything went OK , a new partition will be shown under the device you specified:
```bash
lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT /dev/sdc
NAME FSTYPE SIZE MOUNTPOINT  
sdc          10G  
└─sdc1       10G
```
It is not done yet. The new partition still needs to be formatted:
```bash
mkfs.ext4 /dev/sdc1

mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done
Creating filesystem with 2621184 4k blocks and 655360 inodes
Filesystem UUID: c91c874f-28fb-4d81-8eea-004fee865db0
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
```
And labeled (this is not mandatory, but it can be useful later as a replacement for the UUID value):
```bash
e2label /dev/sdc1 wsl-home
```
## Mounting the virtual disk to the home directory <a name="mount-home"></a>
This step is no different from the usual procedure used to mount external devices in your Linux filesystem.

Once found the device:
```bash
blkid

...
/dev/sdc1: LABEL="wsl-home" UUID="e9999b9b-5a8e-41e1-9881-a2f766838798" BLOCK_SIZE="4096" TYPE="ext4" ....  
...
```
You can use its `LABEL` or `UUID` value for the home mount point. Open the `/etc/fstab` file and add:
```fstab
UUID=<device_UUID> /home/<user_name> ext4 defaults 0 2
```
Alternatively (if you are sure no other devices with the same label are mounted in WSL):
```fstab
LABEL=<device_label> /home/<user_name> ext4 defaults 0 2
```
## Ready, Set, Go 🚀 <a name="ready-set-go"></a>
If you've reached this point without any problems, you're almost done!

The last thing to do is to log out from the current session:
```bash
exit
```
Restart it:
```powershell
wsl.exe -t <session>
wsl.exe -d <session>
```
And configure it according to your needs.

## Troubleshooting <a name="troubleshooting"></a>

{% details No Hyper-V installed on the system %}
To use the `New-VHD` cmdlet in PowerShell, the **Hyper-V** service must be enabled. This feature is supported only with Windows 10/11 Education, Pro, or Enterprise editions:

- Through **Hyper-V** and sub-options in **Windows Features** from the Control Panel.
- Through PowerShell (with Administrator privileges) using the command: `Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All`.
- Through the command line (with Administrator privileges) using the command: `DISM /Online /Enable-Feature /All /FeatureName:Microsoft-Hyper-V`.

However, if you are using Windows 10/11 Home edition, it is possible to modify the system to support **Hyper-V** through a workaround.

> ⚠️ This is an unsupported method to enable Windows 10/11 Home edition to run the **Hyper-V** service. As mentioned, it's not officially supported by Microsoft, there's no guarantee it will work on your machine, it could lead to unexpected issues, and it may potentially violate the terms of the software's license agreement. **Use it at your own risk**.

Create a batch file that will be executed as Administrator, and it will install and enable the service on the machine. Open your preferred text editor and type:
```shell
pushd "%~dp0"  

dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt

for /f %%i in ('findstr /i . hyper-v.txt 2^>nul') do dism /online /norestart /add-package:"%SystemRoot%\servicing\Packages\%%i"  

del hyper-v.txt  

dism /online /enable-feature /featurename:Microsoft-Hyper-V -All /LimitAccess /ALL  

pause
```
Save it (e.g., `Enable-Hyper_V.bat`), and once executed, the service should be available on your machine.
{% enddetails %}

{% details Missing device on system reboot %}
As far as I know, there is currently no officially supported way to have your (virtual) external drives mounted in WSL when the operating system starts.

However, with a bit of hacking, it is possible to achieve this result.

### Option 1: Task Scheduler or Startup Folder
**Task Scheduler**:
- run (`WIN+R`) the snap-in `taskschd.msc`
- select `Create Task...`
- **General** tab: type the task name in `Name`: "WSL Home Mount" for example
- **General** tab: add a description if you want
- **General** tab: select `Run only when user is logged on`
- **General** tab: select `Windows 10` for `Configure for`
- **Triggers** tab: click `New...`, create `On Access` trigger, and set it for your user
- **Actions** tab: click `New...`, create `Start Program`, set `C:\Windows\System32\wsl.exe` for `Program or Script`, and `--mount <virtual_disk_path> --vhd --bare` for `Arguments`
- **Conditions** tab: deselect everything
- **Settings** tab: activate the first 5 checkboxes, and set to `Don't start a new instance` if the task is already running
- Save and reboot.

**Startup Folder**:
- open the folder: `WIN+R`, `shell:startup`
- create a batch file and name it as you want (e.g. `wsl_home_mount.bat`)
- type: `@echo off; C:\Windows\System32\wsl.exe --mount <virtual_disk_path> --vhd --bare`
- Save and reboot 

By doing so, every time Windows will restart, the specified virtual disk will be mounted into WSL and made ready for all the Linux sessions.

### Option 2: Windows Terminal settings
Replace the default startup command line for the specific Linux session in the Windows Terminal
```powershell
pwsh.exe -Command "wsl.exe -d <session> --mount <virtual_disk_path> --vhd --bare  --cd ~ | out-null; wsl.exe -d <session> --cd ~"
```
With this hack, WSL will attempt to mount the virtual disk and change to the home (`~`) directory every time the session starts, if not already mounted.

The (minor) issue with this workaround is that the Windows Terminal won't recognize the started process as a Linux session but rather as a PowerShell process running a script. For this reason, **Option 1** is the preferred choice to solve the problem.
{% enddetails %}

{% details VSCode and other Windows executables do not start %}
This problem may arise with certain Linux distributions, and it is connected to the fact that WSL does not correctly configure the [Linux Kernel Support for Binary Formats](https://docs.kernel.org/admin-guide/binfmt-misc.html) to work with the [WSL interoperability](https://learn.microsoft.com/en-us/windows/wsl/filesystems).

To solve the issue, just type:
```shell
sudo sh -c 'echo :WSLInterop:M::MZ::/init:PF > /usr/lib/binfmt.d/WSLInterop.conf'
sudo systemctl restart systemd-binfmt
```
{% enddetails %}

{% details Home directory permissions and race conditions %}
As the home directory is shared between different Linux sessions, it is advisable for each session to use the same username to prevent conflicts and permission issues. (You may still need to run a `chown -R $(whoami): /home/$(whoami)` if any issues arise when switching from one session to another.)

Given that all sessions share the same home directory, it is not recommended to work with different sessions simultaneously to prevent race conditions and compromise the data integrity on the mounted virtual disk for the home directory.
{% enddetails %}

## References <a name="refs"></a>

- https://www.ubackup.com/enterprise-backup/windows-11-hyper-v-not-showing.html
- https://superuser.com/questions/1515246/how-to-add-second-wsl2-ubuntu-distro-fresh-install
- https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/
- https://learn.microsoft.com/en-us/windows/wsl/wsl-config#wslconf
- https://anthony-f-tannous.medium.com/wsl2-how-to-prepare-and-attach-virtual-drives-vhd-ac17b1fc7a61
- https://learn.microsoft.com/en-us/windows/wsl/wsl2-mount-disk
- https://superuser.com/a/1727367/215993
- https://github.com/microsoft/WSL/issues/8952#issuecomment-1568212651
- https://www.reddit.com/r/bashonubuntuonwindows/comments/gxbufo/running_arch_on_wsl_from_the_source_images_the/
- https://wiki.archlinux.org/title/Users_and_groups

I hope this guide has been helpful in resolving the problem I was stuck in for a while. I will try to keep it updated with new useful information.

That's all, folks!