Skip to content

Instantly share code, notes, and snippets.

@Ragdata
Last active May 5, 2023 04:54
Show Gist options
  • Save Ragdata/1ccd30cba1579c5a1073bddcb7848ddb to your computer and use it in GitHub Desktop.
Save Ragdata/1ccd30cba1579c5a1073bddcb7848ddb to your computer and use it in GitHub Desktop.

Install Multiple Instances of WSL2 Linux on Windows 11

Here's an easy method which allows you to install multiple instances of any Linux Distro in any location under WSL2.

** Click on each heading to reveal instructions **

If you HAVEN'T installed WSL2 prior to this, open up a Powershell terminal and execute the following commands:

PS C:\> Enable-WindowsOptionalFeature -Online -FeatureName 'Containers' -All
PS C:\> Enable-WindowsOptionalFeature -Online -FeatureName 'Microsoft-Hyper-V' -All
PS C:\> Enable-WindowsOptionalFeature -Online -FeatureName 'VirtualMachinePlatform' -All
PS C:\> Enable-WindowsOptionalFeature -Online -FeatureName 'Microsoft-Windows-Subsystem-Linux' -All

The first thing you need to do is download a linux distro. This is a list of all distros released by Mocrosoft specifically for WSL2:

(And if you're not already using it, install Windows Terminal from the Windows Store also - you won't regret it)

^ Top

So, let's assume for a moment that your C: drive is dangerously low on space and you need to choose another location to install WSL2. Using Windows Explorer, grab the Linux distro you just downloaded from your downloads directory and copy it to wherever you're going to install it. For the purposes of this guide, I'm going to assume that you've downloaded Ubuntu 22.04 LTS and you're going to install into E:\WSL.

Open Windows Terminal to a Powershell prompt and execute the following commands to create your install location:

PS C:\> E:
PS E:\> mkdir WSL

^ Top

Now, the first thing you need to do is rename the archive of the distro you downloaded so that it has a .zip extension:

PS E:\> cd WSL
PS E:\WSL> mv Ubuntu2204-221101.AppxBundle Ubuntu-22.04.zip

Then, using Windows Explorer, double click on the distro archive, then select and extract the _x64.appx file as shown below:

WSL-1

Once you've extracted the installer, you don't actually need the archive you pulled it from any longer. You can delete it or stick it in your own file archives if you like - and you're going to need to change the extension on that .appx file as well (either using Windows Explorer or Powershell - up to you)

WSL-2

PS E:\WSL> rm Ubuntu-22.04.zip
PS E:\WSL> mv Ubuntu_2204.1.7.0_x64.appx Ubuntu-22.04.zip
PS E:\WSL> Expand-Archive -Path Ubuntu-22.04.zip -DestinationPath E:\WSL\Ubuntu

The end result of which will be:

WSL-3

Then all you need to do is execute the ubuntu.exe file to install your first instance:

PS E:\WSL> cd Ubuntu
PS E:\WSL\Ubuntu> .\ubuntu.exe

^ Top

Now let's create your BASE INSTANCE.

Open either a simple Command Prompt or a Powershell Prompt and let's take care of a little housekeeping.

First, get a list of all WSL instances currently running on your machine:

PS E:\WSL> wsl -l -v

It should look something like this:

  NAME      STATE       VERSION
* Ubuntu    Running     2

(* Each instance will be named after its parent folder ... more on this later)

Hopefully you've installed Windows Terminal from the Windows Store - if not, you can open a new terminal window for any WSL instance using this command:

wsl -d <instanceName>

PS E:\WSL> wsl -d Ubuntu

Step 1 - Set Password for Root User

This first step is not necessarily based on best practices, but I've always found it to be useful. Skip this step if you want to:

user@WSL: /$  sudo passwd root

Step 2 - Update & Upgrade

Next you want to make sure you've got the latest version of everything installed:

user@WSL: /$  sudo apt update && sudo apt upgrade -y

If you've installed an Ubuntu LTS release, you're also going to want to make sure you've got the latest kernel updates:

user@WSL: /$  sudo do-release-upgrade

In any case, you'll want to make sure to clean up after yourself:

user@WSL: /$ sudo apt autoremove -y && sudo apt autoclean -y

... and if kernel updates were installed, you'll want to reboot:

user@WSL: /$ sudo reboot

^ Top

WSL2 configuration happens in 2 files:

  • .wslconfig - is stored in your %USERPROFILE% directory in Windows and applies to ALL WSL INSTANCES
  • /etc/wsl.conf - applies only to the instance it is part of

First, let's take care of your instance configuration file - /etc/wsl.conf:

(copy and paste everything below onto your linux command line)

user@WSL: /$ sudo tee /etc/wsl.conf > /dev/null <<EOF
# Automatically mount Windows drives when distribution is launched
[automount]
# Set whether to automount fixed drives with drvfs under the "root" dir specified below
enabled = true
# Set the directory where fixed drives will be automatically mounted
root = /mnt
# drvfs-specific options for mounted drives
options = "metadata,uid=1000,gid=1000,umask=22,fmask=11,case=off"
# Set whether to process /etc/fstab on boot
mountFsTab = true

# Network host settings
[network]
# Set hostname
hostname = wsl
# Set whether to automatically generate /etc/hosts file
generateHosts = true
# Set whether to automatically generate /etc/resolv.conf file
generateResolvConf = true

# Interop settings only available in build 17713 and later
[interop]
# Set whether to support interop processes like launching windows apps from linux
enabled = true
# Set whether to append Windows path to Linux path
appendWindowsPath = true

# User settings only available in build 18980 and later
[user]
# Set the default user when launching a distro
default = ragdata

# Boot settings only available on Windows 11 and Server 2022
[boot]
# Set whether to enable systemd
systemd = true
# Set a command to run when a new WSL instance launches
#command = service docker start
EOF

Next, let's take care of your global configuration file - .wslconfig

[wsl2]
# Limit VM memory use                                             default - smallest of 50% of total or 8GB
memory = 4GB
# Limit VM to 2 virtual processors                                default - The number of processors on Windows
processors = 2
# Set localhost forwarding                                        default - true
localhostForwarding = true
# Specify a custom kernel to use with your installed distros      default - blank
# kernel = C:\\temp\\myCustomKernel
# Set additional kernel parameters                                default - blank
# kernelCommandLine = vsyscall=emulate
# Set available swap space                                        default - 25% of memory rounded up
swap = 8GB
# Set swapfile location                                           default - %USERPROFILE%\AppData\Local\Temp\swap.vhdx
swapfile = C:\\temp\\wsl-swap.vhdx
# Enable Windows to reclaim unused memory allocated to a WSL VM   default - true
pageReporting = true
# Enable support for GUI applications (WSLg) - Windows 11 only    default - true
guiApplications = true
# Enable debug console - Windows 11 only                          default - true
debugConsole = false
# Enable nested virtualization - Windows 11 only                  default - true
nestedVirtualization = true
# Set VM idle timeout - Windows 11 only                           default - 60000
vmIdleTimeout = 60000

Entries with a PATH value must be Windows paths with escaped backslashes, eg: C:\\Dir\\file

Entries with a SIZE value must be a size followed by a unit, eg: 8GB or 512MB

Create your .wslconfig file under C:\Users\<username>\.wslconfig

OPTIONAL - Configure drvfs Mounts

I find it useful to be able to mount certain Windows directories under certain Linux directories depending upon the role intended for a WSL2 instance. For example, if an instance is to be a docker host, you may want to mount a specific location for docker volumes and other data under certain directories:

user@WSL: /$ sudo tee /etc/fstab > /dev/null <<EOF
# UNCONFIGURED FSTAB FOR BASE SYSTEM
LABEL=cloudimg-rootfs   /                       ext4    defaults                                                        0 0
D:\                     /mnt/d                  drvfs   metadata,uid=1000,gid=1000,umask=22,fmask=11,case=off           0 0
/mnt/d/Projects         /home/ragdata/projects  drvfs   metadata,uid=1000,gid=1000,umask=22,fmask=11,case=off,bind      0 0
EOF

user@WSL: /$ mkdir -p /home/ragdata/projects

user@WSL: /$ sudo reboot

NOTE: If you do mount a folder as demonstrated above, make sure that you create the destination folder before you reboot, or FsTab will throw an error when it tries to mount the directory and can't find it.

^ Top

NOTE: You will only be able to install GUI support for Ubuntu on WSL2 if you can find your graphics card listed HERE.
I usually skip this step because it inflates the size of the .vhdx file for an instance quite considerably (bloat).

  1. Install gcc if you haven't already:
user@WSL: /$ sudo apt install gcc -y
  1. Install the CUDA keyring:
user@WSL: /$ sudo wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.0-1_all.deb
user@WSL: /$ sudo dpkg -i cuda-keyring_1.0-1_all.deb
  1. Update & Install CUDA
user@WSL: /$ sudo apt update
user@WSL: /$ sudo apt install cuda -y
  1. Install basic X11 applications
user@WSL: /$ sudo apt install x11-apps -y

^ Top

If you have any SSH keys that will be required by your WSL2 instances, now would be a good time to include them (eg: SSH key for GitHub)

For the purposes of this guide, I'll assume that you've already got an SSH Key that you use to authenticate with GitHub set up and working. All that needs to happen is that you set up a .ssh folder under your WSL directory that you can copy your existing key into . So, using Powershell:

PS E:\WSL> mkdir .ssh
PS E:\WSL> cp ~\.ssh\key_name .ssh\key_name
PS E:\WSL> cp ~\.ssh\key_name.pub .ssh\key_name.pub
PS E:\WSL> $text = "Host github.com"
PS E:\WSL> $text > .ssh\config
PS E:\WSL> $text = "  IdentityFile ~/.ssh/key_name"
PS E:\WSL> $text >> .ssh\config

Then the easiest way to copy your .ssh folder to wherever it needs to be is like so:

PS E:\WSL> cp -R .ssh \\wsl$\Ubuntu\home\ragdata

Then switch to your WSL terminal window and take care of permissions:

user@WSL: ~$ sudo chown -R ragdata:ragdata .ssh
user@WSL: ~$ chmod 0600 .ssh\*
user@WSL: ~$ chmod 0644 .ssh\*.pub config

And add a couple of lines to .bashrc to make the key available to the ssh-agent:

user@WSL: ~$ echo "eval $(ssh-agent) &> /dev/null" >> .bashrc
user@WSL: ~$ echo "ssh-add ~/.ssh/key_name &> /dev/null" >> .bashrc
user@WSL: ~$ . ~/.bashrc

^ Top

The easiest way to install docker on WSL is to simply install Docker Desktop for Windows 11 and tick the boxes under settings for which WSL2 instance you want to have access to it. However, for the purposes of this guide, I'm going to assume that you intend to run docker on your Linux instances independently of Docker Desktop (because you might want to host a swarm)

  1. Install prerequisites:
user@WSL: ~$ sudo apt install ca-certificates curl gnupg2 lsb-release -y
  1. Add Docker's official GPG key:
user@WSL: ~$ sudo mkdir -p /etc/apt/keyrings
user@WSL: ~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  1. Use the following command to set up the Docker repository:
user@WSL: ~$ echo \
> "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
> $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /de>v/null
  1. Install Docker Engine
user@WSL: ~$ sudo apt update
user@WSL: ~$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
  1. Create Additional Non-Root User

Because docker needs to grant some hefty privileges to a non-root user, it's a good idea to set up a user specifically for this:

user@WSL: ~$ sudo adduser dockeruser

Then grant that user sudo privileges:

user@WSL: ~$ sudo usermod -aG sudo dockeruser

Finally, add this user to the docker group:

user@WSL: ~$ sudo usermod -aG docker dockeruser
  1. Create a config file for the dockerd daemon:
user@WSL: ~$ sudo mkdir -p /etc/docker
user@WSL: ~$ sudo tee /etc/docker/daemon.json > /dev/null <<EOF
{
  "hosts": ["tcp://0.0.0.0:"],
  "tls": false
}
EOF

Then, you can launch dockerd directly using the following command:

user@WSL: ~$ sudo dockerd

and stop it with CTRL-C

If you want to launch dockerd in the background, use:

user@WSL: ~$ sudo dockerd &

and stop it with:

user@WSL: ~$ sudo pkill dockerd

OR you can do it the RIGHT way and launch it via systemd:

user@WSL: ~$ sudo service docker start

and verify that it's working with:

user@WSL: ~$ sudo docker run hello-world

^ Top

^ Top

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment