Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

WSL 2 is the Linux aspect “Linux aspect” of Windows. It supports the direct execution of a Linux program on Windows by providing the libraries of a distribution like Ubuntu and the system services of a real Linux Kernel. It is a hybrid environment that is part Windows, part Linux. If you want the true Linux experience, use Hyper-V “Quick Create” to build a real Linux VM.

WSL 2 lets you slide between Windows and Linux seamlessly. All your Windows files are available to Linux programs, and all you Linux files are available to Windows programs. You can create *.bat or Powershell scripts that mix Windows and Linux commands and pipe data back and forth. Since you can run bash, sed, awk, grep, etc. now in Windows, you don’t need old emulation systems like Cygwin.

WSL, like Docker Containers, run in Linux “single user” mode, where the only interface is a terminal session and the only programs running are ones you start manually. All Linux systems can be started this way to do maintenance and recovery, but if you boot Linux normally it will start configured background services (daemons). In single user mode, there is no sshd to login through the network or snapd to manage snap packaged programs. The original WSL 1 was architecturally constrained. WSL 2 can start systemd and start services, but for now Microsoft seems to want to preserve your ability to switch back and forth between WSL 1 and WSL 2 in a few minutes, so by default you get the WSL 1 constraint.

When you run an distribution like Ubuntu under WSL, it does not offer to install all the packages that you might have available on an Ubuntu VM. For now, you will not be offered packages that expect a full graphic desktop or depend on daemons. In particular, since snapd is not running, you will not see snaps. Simple graphic programs will still be offered:

Code Block
gilbert@wholewheat:/mnt/c/Users/gilbert$ xclock
Command 'xclock' not found, but can be installed with:
sudo apt install x11-apps

WSLG - Graphics Applications (Coming)

X11 connects a program running on Linux to a terminal emulator which may be on another computer. It does not depends on background services, so it can run from WSL to Windows if you install an X Terminal emulator program, like X-Win32 from the Yale Software Library. You need to set the DISPLAY environment variable in WSL Linux and run an x11 program. For example, after starting X-Win32, you can open a Windows command prompt and (assuming your Windows has a particular IP address) type:

bash -c "export DISPLAY=192.168.1.44:0 && xclock"

And the Linux GUI clock appears on your Windows screen.

However, you don’t need to install X-Win32 to run an entire Linux VM on Hyper-V. On a PC Linux takes the X11 protocol and writes the output to the hardware monitor through the video adapter card, and in a VM it writes to a virtual video card that Hyper-V translates to an image in a window on the screen. Since there are other protocols than X11 and Microsoft does not want to be dependent on a third party terminal emulator, they are adapting Remote Desktop Protocol for their WSL Graphics support.

Also, instead of using the old X11 protocol, the Linux side of their work uses the Wayland protocol implemented through the Weston software component.

However, this is still Microsoft and Windows. Unlike a Hyper-V VM, Microsoft is not interested in popping up a window on your screen with a Gnome desktop. There already is a Windows desktop and they don’t need another one. I already showed you the trick above. If you can run xclock from the Windows Command Line , then you can also create a Shortcut in the Windows start menu to do effectively the same thing.

Most desktop users don’t know that there is a single application, single window version of Remote Desktop Protocol. It was created to run an application on a server in the machine room (or today on a server in Azure) but make the application program look to the user just like any other program on the desktop. There are a pair of names for this: RAIL (if the desktop and server are connected over the network) and VAIL (if the server is a VM and the connection uses the Hyper-V vmbus).

Obviously, to set up a Wayland/Weston support environment would require substantial changes to the current WSL “distributions” available from the Windows Store (Ubuntu, Alpine, Arch) and Microsoft doesn’t want the graphics support to have to be installed into each distro. This problem has already been addressed by Docker to install and support the Docker Engine running in WSL 2 on any distribution. After installing Docker Desktop for Windows and clicking the “Use WSL 2” checkbox, you will see that additional “distributions” have been added to your list:

Code Block
PS C:\Users\gilbert> wsl -l
Windows Subsystem for Linux Distributions:
Ubuntu-20.04 (Default)
docker-desktop-data
docker-desktop

So in the same spirit, when WSLG is added by Microsoft to support GUI applications running in WSL, they will add a “system distro” that runs side by side with the Linux distro. The Weston manager and the communication with Windows using RAIL/VAIL will be in that separate environment, making changes to the Ubuntu or Alpine distribution minimal.

It would be tedious to have to define a Windows shortcut yourself for every Linux GUI program you want to launch from the Windows Start menu. So the plan is eventually to read all the .desktop files installed in a WSL Linux distribution and generate the Start Menu entries automatically. Exactly how this will be done has not been disclosed and it is not trivial. You can run more than one Linux distribution in WSL at the same time, and you might want Windows shortcuts for “Firefox running in WSL Alpine” and “Firefox running in WSL Ubuntu” and you can run both side by side in the Windows screen. It is easy to set this up, but it is hard to figure out how to generate the shortcuts automatically.

Some WSL Implementation Internals

Bell Labs created Unix when computers were big expensive machines shared by users. Then Personal Computers came along, and the need was to control networks of small cheap computers. The Unix developers started work on a successor to Unix that would look like one big operating system that was spread out through the network across a number of client and server machines. The system was called “Plan 9” and to make it work they needed programs and services to communicate over a protocol that worked the same between two components running on the same computer or the same two components split on two different machines connected by the network. The protocol was called “9P”. Since WSL is specifically intended to create the impression that there are Windows programs and files and Linux programs and files but they are all part of one Windows OS, Microsoft decided to adopt “9P” as the framework.

In concrete terms, consider the following two command entered at the Windows Command Prompt:

Code Block
C:\Users\gilbert>tasklist | find "firefox"
firefox.exe                  24640 Console                    1    587,668 K
firefox.exe                  12656 Console                    1     67,300 K
C:\Users\gilbert>tasklist | wsl grep "firefox"
firefox.exe                  24640 Console                    1    587,832 K
firefox.exe                  12656 Console                    1     67,300 K

The first command runs the Windows tasklist.exe and uses the traditional PC DOS “find” command to select specific lines from the program output. The second line uses wsl.exe to send the Linux command grep “firefox” to WSL. The standard output from the tasklist.exe is piped (using 9P) to the standard input of the Linux grep program. The standard output of grep is piped back to Windows and is read by the Command Prompt, which prints the output to the screen. You can find many systems that provide Linux program converted to run in Windows, but here we are not using some “grep.exe” windows emulator program. We are piping data back and forth between a real Windows program and a real Linux program running side by side in the same windows environment. It works the other way:

Code Block
C:\Users\gilbert>bash
gilbert@wholewheat:/mnt/c/Users/gilbert$ tasklist.exe|grep firefox
firefox.exe                  24640 Console                    1    593,900 K
firefox.exe                  12656 Console                    1     67,776 K

We run the bash.exe program to move to the WSL Linux shell environment. Now in WSL Linux, we type a normal Linux command except that the executable program name ends in .exe. When Bash sees a windows executable program as a Linux command, it uses 9P to ask Windows to run the program and pipe the output back to it. So you can both run Windows programs in the middle of a Linux command, and run Linux programs in the middle of a Windows command, and pipe data back and forth automatically.

Windows expects commands to be .exe or .bat files (or other configured extensions), but Linux executables normally have no extension. So the convention in Windows is to type “wsl commandname” to run a Linux command using the wsl.exe program. Linux does not expect programs to have extensions, so in WSL Microsoft has added support to recognize either the .exe extension or the format of a Windows .exe file.

User mode programs talk across systems using 9P. Microsoft already had a separate convention to allow OS Kernels in Hyper-V VMs to talk back to the Windows host using the Hyper-V virtual bus. Microsoft has a lot of VMs in its Azure Cloud that run Linux, and to make that work most efficiently, they had already adapted the Linux Kernel to use Hyper-V communication for services like disk and LAN I/O. They built that support into the WSL Linux Kernel.

For example, a program opens a file path name, and the OS file system turns the file name into a file location (a set of relative block numbers in the disk partition). In a real computer, this in turn gets translated into a hardware LBA, a relative sector number on the disk, which is in turn converted to a SATA or SCSI controller hardware command. If you do this in a VM, then the Hyper-V, VMWare, or VirtualBox supervisor has to translate the SATA or SCSI command back to an LBA. It is more efficient to put a Hyper-V specific driver in the Linux Kernel that stops before it emulates a disk controller and ships the LBA from the Linux Kernel to the Windows Kernel where it can be translated to a offset in an open VHDX file containing a disk image.

There is one more important optimization to make WSL start up instantly. A normal VM has a designated boot disk. When the VM starts up, an emulated BIOS or EFI firmware loads boot data off the virtual boot disk, which on a Linux system is a component named “Grub”. About 50 years ago IBM showed that there was a different way to load an OS into a virtual machine, and today various vendors are rediscovering it. The trick is to book an OS into a virtual machine until it is completely initialized but has not yet run any programs. Now stop the VM and take a snapshot of its memory and save that checkpoint to disk. When you want to run that OS, simply restore the checkpoint back to virtual memory and start it up. So the WSL Linux Kernel does not “boot” up, does not have a Grub, and does not go through an emulated BIOS. It simply appears “poof” already loaded into memory and begins running. Well, there is a slight bit of initialization for differences between AMD or Intel CPUs, but that is much less work than a real boot.

The Distros

Once you have WSL 2 itself, you go to the Windows Store and select one or more “distributions” that have been contributed by third parties. Canonical Ubuntu is the vendor that has worked most closely with Microsoft to make WSL run smoothly and is the recommended choice here, but you can also download versions of Alpine and SUSE and a third party (Whitewater Foundry) distributes versions of Red Hat systems that have been converted to WSL.

A WSL Distribution looks like a persistent container. The Ubuntu that runs in WSL has roughly the same features, programs, and restrictions as the Ubuntu image you download from Docker Hub. This was a requirement for WSL 1 and has been carried forward to WSL 2. If you find that there is no support for a package you need, then you may require a VM to run it.

What you download from the Store (Ubuntu 20.04 or Alpine) is a store package with a compressed virtual disk image containing a specific selected set of user mode Linux libraries and executables placed in /bin, /etc, /lib, and other directories ready to run a Linux system when you start it up. The package files are stored in the Windows system directories.

WSL must be instructed (though normally this is implicit and not visible to the user) to use the package to create an instance of the distribution by decompressing the VHDX file and storing a working copy in the home directory of the current user.

At any time you can discard your current instance and start over with an empty fresh copy of the original package disk. If you have more than one userid defined on your computer, even if they are just alternate names that you use for different purposes, then each of these users gets a separate instance of a WSL distro. This is necessary because even if a user is not an admin of the Windows system, he always has root privileges in his private copy of a WSL Linux instance.

There may be slight differences in the punctuation of the “Ubuntu 20.04” package that you download and the “Ubuntu-20.04” instance you create from it. Microsoft does not make a big deal of it, and generally you don’t ever think about packages unless you are in the Store application itself. When the “wsl” command is documented to have a <distro> parameter, this is really the name of an instance.

You can create more than one instance of Ubuntu 20.04 under the same user, but Microsoft does not explain how to do it in their documentation. The Windows “wsl --export” command is documented to backup or checkpoint the current state of a WSL instance to a tar file with a name and location you choose. Nominally this is a way to restore a good environment after you try something that fails terribly, or to uninstall a component and its dependent packages that you find to be a bad idea. However, you can use the “wsl --import” command to create a new instance with a new name instead of just restoring an existing instance. In parameters of the command, you choose the new instance name, and specify a directory where you want the instance files stored.

The “wsl --import” command can also be used to create a new WSL instance based on a “backup” tar file that you did not create, but instead received from a coworker. You will also find that new versions of Ubuntu that are not yet available on the Store (or are still in Beta) are available every few weeks as files named something like ubuntu-20.04-server-cloudimg-amd64-wsl.rootfs.tar.gz in the Canonical site https://cloud-images.ubuntu.com/releases/. Remember to unzip the file because “wsl --import” takes a tar and not a tar.gz file.

Whichever “distro” instance of Linux is currently designated as the Default becomes the environment in which you can conveniently escape from Windows to Linux (and then back from Linux to Windows) as you type in commands or run scripts. Once you get used to it, you can slip back and forth between the two environments freely.

If you install more than one package and create and run more than one instance, you will discover that all instances share the same copy of the Linux Kernel. They run as a type of container on top of the Kernel, and share the same LAN adapter, IP address, and port numbers. So if you are running Tomcat on port 8080 in Ubuntu WSL, that port is busy and cannot be used in Alpine WSL.

The Good

If you are running Windows Powershell or Command Prompt and you want to run a single Linux command, just precede the command with “wsl”.

Code Block
PS C:\Users\gilbert> wsl ps
  PID TTY          TIME CMD
 7064 pts/3    00:00:00 ps

The Windows “wsl ps” command runs the wsl.exe program. It takes everything that follows the program name and sends it to Linux to run as a single process. The standard output of the Linux process is sent back to Windows where it becomes the standard output of the wsl.exe program. From the point of view of the program running wsl.exe, it is just a Windows program that runs like any other executable.

Each Windows disk letter appears to be mounted to WSL Linux under the /mnt directory. Thus in WSL Linux, /mnt/c is the Windows C:\ disk. Each WSL Linux instance is accessible from Windows as if it were a share from a network host with the computer name “wsl$”. Therefore, the /usr/local/ directory of the Ubuntu-20.04 instance is \\wsl$\Ubuntu-20.04\.

Code Block
PS C:\Users\gilbert> wsl pwd
/mnt/c/Users/gilbert

PS C:\Users\gilbert> dir \\wsl$\Ubuntu-20.04\usr\local
    Directory: \\wsl$\Ubuntu-20.04\usr\local
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----            9/5/2020  2:23 PM                bin
d----           4/23/2020  2:40 AM                etc
d----           4/23/2020  2:40 AM                games
d----           4/23/2020  2:40 AM                include
d----           4/23/2020  2:40 AM                lib
d----           4/23/2020  2:40 AM                sbin
d----           7/16/2020 11:19 PM                share
d----           4/23/2020  2:40 AM                src

The wsl.exe program only runs a single Linux executable as a single process. If you want to enter an entire line of Linux commands, remember that interpreting a line of command with “|” or “&&” separators is a function of a shell program. For convenience there is a separate bash.exe on Windows that makes it easy. You can of write a script file and run it, but remember that script files have to be edited with Linux line ends and not Windows line ends. For a single line, use the -c parameter and put the Linux command line in quotes:

Code Block
PS C:\Users\gilbert> bash -c "ps -ef|grep vscode"
gilbert   6640  6639  0 Sep04 pts/2    00:00:00 sh -c "$VSCODE_WSL_EXT_LOCATION/scripts/wslServer.sh" a0479759d6e9ea56afa657e454193f72aef85bd0 stable .vscode-server 0
gilbert   6641  6640  0 Sep04 pts/2    00:00:00 sh /mnt/c/Users/gilbert/.vscode/extensions/ms-vscode-remote.remote-wsl-0.44.5/scripts/wslServer.sh a0479759d6e9ea56afa657e454193f72aef85bd0 stable .vscode-server 0
gilbert   6646  6641  0 Sep04 pts/2    00:00:00 sh /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/server.sh  --port=0 --use-host-proxy --enable-remote-auto-shutdown
gilbert   6648  6646  0 Sep04 pts/2    00:00:05 /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/node /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/out/vs/server/main.js  --port=0 --use-host-proxy --enable-remote-auto-shutdown
gilbert   6666  6648  0 Sep04 pts/2    00:00:01 /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/node /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/out/bootstrap-fork --type=watcherService
gilbert   6692  6648  0 Sep04 pts/2    00:00:11 /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/node /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/out/bootstrap-fork --type=extensionHost --uriTransformerPath=/home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/out/vs/server/uriTransformer.js --useHostProxy=
gilbert   7070  7069  0 11:10 pts/3    00:00:00 /bin/bash -c ps -ef|grep vscode
gilbert   7072  7070  0 11:10 pts/3    00:00:00 grep vscode

The Windows bash.exe has no parameter for distribution name, so it can only communicate with the default instance of WSL Linux. It sends the command line to Linux, which runs ps and pipes the output to grep. The standard output from grep is captured and sent back to windows where it becomes the standard output of bash.exe.

If you don’t put the entire command line in quotes, the Windows Command Prompt or Powershell environment will stop when it hits the unquoted “|” because that is a special character in both Windows and Linux shells. There is no grep.exe in Windows (unless you added it yourself) but roughly the same thing can be done with the Powershell Select-String cmdlet:

Code Block
PS C:\Users\gilbert> wsl ps -ef|select-string "vscode"

gilbert   6640  6639  0 Sep04 pts/2    00:00:00 sh -c "$VSCODE_WSL_EXT_LOCATION/scripts/wslServer.sh" a0479759d6e9ea56afa657e454193f72aef85bd0 stable .vscode-server 0
gilbert   6641  6640  0 Sep04 pts/2    00:00:00 sh /mnt/c/Users/gilbert/.vscode/extensions/ms-vscode-remote.remote-wsl-0.44.5/scripts/wslServer.sh a0479759d6e9ea56afa657e454193f72aef85bd0 stable .vscode-server 0
gilbert   6646  6641  0 Sep04 pts/2    00:00:00 sh /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/server.sh  --port=0 --use-host-proxy --enable-remote-auto-shutdown
gilbert   6648  6646  0 Sep04 pts/2    00:00:05 /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/node /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/out/vs/server/main.js  --port=0 --use-host-proxy --enable-remote-auto-shutdown
gilbert   6666  6648  0 Sep04 pts/2    00:00:01 /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/node /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/out/bootstrap-fork --type=watcherService
gilbert   6692  6648  0 Sep04 pts/2    00:00:11 /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/node /home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/out/bootstrap-fork --type=extensionHost --uriTransformerPath=/home/gilbert/.vscode-server/bin/a0479759d6e9ea56afa657e454193f72aef85bd0/out/vs/server/uriTransformer.js --useHostProxy=

Powershell ends the wsl.exe command at the “|” and wsl.exe only sends the “ps -ef” to Linux to execute. The standard output of ps is piped back to Windows and Powershell sees it as the standard output of the wsl.exe program. Powershell then pipes that string on to Select-String which filters the lines that contain “vscode”. The output is roughly the same, except that you don’t get lines for the grep process that isn’t running.

Similarly, if you are in WSL Linux and you want to run a Windows command, just add the “.exe” file suffix on the end of the command:

Code Block
gilbert@wholewheat:/mnt/c/Users/gilbert$ tasklist.exe|grep firefox
firefox.exe                  26904 Console                    1    424,532 K
firefox.exe                  28724 Console                    1    107,920 K
firefox.exe                  28404 Console                    1    315,080 K
firefox.exe                  17492 Console                    1    102,580 K
firefox.exe                  22560 Console                    1     16,240 K
firefox.exe                   5076 Console                    1    122,604 K
firefox.exe                  24172 Console                    1    531,812 K
firefox.exe                  25364 Console                    1    100,400 K
firefox.exe                  31040 Console                    1     72,940 K

Tasklist.exe is the Windows Command Prompt equivalent of Linux “ps -ef”. Linux is agnostic about whether executables have or don’t have a file type. The Microsoft Linux Kernel, however, knows about .exe and Windows executable file formats. When you try to run such a file in Linux, it sends the request back to Windows for execution, and the standard output of tasklist.exe is then sent back to Linux as standard output from the program. The shell in Linux then pipes that standard output to grep which filters the lines and the resulting standard output is printed out.

You may wonder at this point how Linux found tasklist.exe in its Path. The answer is that when the WSL Linux environment is created, the current Windows path is imported into Linux and is converted to the Linux equivalent fully qualified file path. So where C:\Windows\System32 is in your Windows Path, WSL Linux will append a /mnt/c/Windows/System32 to its path. Therefore, all windows executables are automatically available to WSL Linux just by typing their full *.exe name.

To be comprehensive, we should note that Powershell has its own Get-Process cmdlet to do the same thing. However, Powershell cmdlets are not standalone executables and can only run within the environment set up by running the Powershell program. Powershell has the same “-c” parameter to run one line and then exit, so look at one last example that runs in Linux but runs something in Powershell on Windows:

Code Block
gilbert@wholewheat:/mnt/c/Users/gilbert$ pwsh.exe -c Get-Process|grep firefox
     96   194.24     119.68      82.39    5076   1 firefox
     57    71.11     101.78      19.34   17492   1 firefox
     16    19.88      15.86     254.45   22560   1 firefox
    158   582.25     527.08     328.27   24172   1 firefox
     85    78.95      96.77       1.61   25364   1 firefox
    239   394.20     421.05     454.61   26904   1 firefox
    137   395.53     314.20      96.81   28404   1 firefox
     95   393.02     106.35     306.20   28724   1 firefox
     75    45.83      50.68       0.36   31040   1 firefox

Here WSL Linux discovers that the pwsh.exe file is a Windows executable, so it sends the command to Windows. The “-c Get-Process” runs the cmdlet in Powershell and returns text as standard output, which is sent back to Linux and piped on to grep.

The Ugly

Assuming I am just a Dummy, when I am asked to create a userid and password in WSL 2 Linux, I will probably use the same userid and password I have in Windows. Then there is a “gilbert” in Windows and a “gilbert” in Linux with C:\Users\gilbert as the Windows home directory and /home/gilbert as the Linux home directory and they are two different directories on two different file systems with different content.

From Windows, you specify a user with the “wsl.exe -u xxxx” parameter. There is no password, and you can specify any username in Linux, including “root”. If you choose a different user than root, then Linux enforces the standard file permission rules for users and groups, but because there are no passwords this is just a pretend security check. If you do not specify a user, then the default user (the user created when you start the distro up for the first time) is used. The real data security comes from the fact that each WSL instance is private to the user that created it, and the disk image of files is stored in his home directory.

When you issue the wsl.exe or bash.exe commands in Windows, the subsequent command is located by using the Linux path. If a file in the Linux path has a name that ends in “.exe” and is a Windows executable, then it runs in Windows. To make this seamless, WSL adds all the files in the Windows path onto the end of the initial Linux $PATH environment variable (although the file names are converted from the C:\xxx syntax in the Windows environment to a /mnt/c/xxx syntax in the Linux $PATH.

The Tomcat Example

Most Yale programs run under the Tomcat Web server. Tomcat is written in Java and by default serves data over internet port 8080. Since Java runs in every type of system, it is useful to consider what is different when you move it from one environment to another.

Consider running Tomcat in the old WSL 1 where programs ran through an emulation layer. Here there is only one OS and only one Kernel, and the LAN adapters, IP addresses, and port numbers are shared by all programs without regard to whether they run in Windows mode or Linux mode. You can run Tomcat in Linux mode on port 8080 in exactly the same way you run it as a Windows program.

This means you could run Tomcat in WSL 1 Linux and connect to it with your Windows browser using the URL “http://localhost:8080/”.

Now convert the WSL Linux from WSL 1 to WSL 2. Because WSL 2 run in a Linux Kernel and because at this time Microsoft was only able to create a virtual LAN adapter with its own IP address, the IP address of WSL Linux no longer matches the IP address of Windows.

Code Block
PS C:\Users\gilbert> Get-NetIPAddress|where -Property PrefixOrigin -eq "Dhcp"|select ipaddress
192.168.1.42
PS C:\Users\gilbert> bash -c "ip address|grep eth0"
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 172.28.72.51/20 brd 172.28.79.255 scope global eth0

Windows has a native IP address of 192.168.1.42 on my home network and any other computer on that network can access it, however WSL Linux has an IP address of 172.28.72.51 on a virtual private network that connects it just to the Windows host. There is a NAT so it can use that virtual network to get to the outside world, but only the host computer has a connection to the virtual network with that address.

If you have more than one distro or instance, they all share the same Kernel and therefore the same virtual LAN and IP address. You can run Tomcat on any one of them, but if you try to run it twice the second one will fail because the 8080 port is in use.

However, it is seldom a requirement to access something you are running in Tomcat from a different machine. Since the common practice when testing Tomcat from a browser on the Windows host is to connect to the “localhost” hostname, Windows currently has a special port forwarding hack to handle that one case.

Any program running in a WSL 2 instance that tries to bind to a port number of the loopback IP address 127.0.0.1 or the wildcard 0.0.0.0 (because it includes the loopback address as well as real addresses) gets this system call trapped by the WSL Linux Kernel and a message is sent to a proxy program running in Windows to bind to the same port number on the Windows loopback and forward traffic from Windows to WSL.

So Tomcat in one of the WSL 2 instances binds to Port 8080 on wildcard IP address 0.0.0.0. Because this includes the WSL loopback adapter, a message is sent to the Windows proxy task and it binds to port 8080 on IP address 127.0.0.1. For now, it does not bind to any real IP address on any real adapter, so this only affects programs running on the Windows host system and only if they use “localhost” or “127.0.0.1” in their URL. So if Firefox naviagates to “http://localhost:8080” it connects to the proxy, which forwards the request to http://172.28.72.51:8080/ which is Tomcat running in WSL 2.

If you want a more general solution where other computers on other hosts can connect to your Tomcat running on WSL, you are going to have to set up your own port forwarding proxy.

Unless you run Tomcat in the Docker Engine under WSL 2. When WSL 2 is installed on Windows 10, Docker regards it as the best environment to run Linux containers. If you run Tomcat in a container on WSL 2 rather than running it as a native Linux program on the same WSL 2 instance, then Docker has its own proxy running in the Windows host environment. When the Engine processes a “docker run -p 8080:8080 …” then it sends a message to its proxy process in Windows to bind to the 8080 port on Windows and forward traffic to the container. Tomcat under Docker in WSL 2 can be reached by other computers in the real network, whereas Tomcat running natively in WSL 2 Linux is not visible.

Docker Desktop for Windows

When you install Docker Desktop for Windows on a Windows 10 system, there is a checkbox to use WSL 2. You want to select this option because it is faster, simpler, and more generally useful. The Windows installer for Docker will also install the docker command and Engine on the default instance of WSL Linux. Later on you can use the Docker configuration window to also install the same Docker components on other WSL instances.

You should not try to install Docker in a WSL Linux instance with a package manager like apt. You want let Docker Desktop for Windows install it instead.

After installation, there is a docker command in Windows and one in WSL Linux. The WSL Linux command only talks to the WSL Linux Engine and therefore only to Linux containers. The docker command in Windows can be switched between the Windows Engine (which runs only Windows Containers) and the WSL Linux Engine (which runs only Linux containers).

Each Engine maintains its own repository of images and manages its own set of running containers. If you switch the Windows docker command from one Engine to another, the containers that were started continue to run. If you switch to Windows Containers, then remember that the WSL Linux docker command continues to be connected to the Linux Engine and you can use it to manage Linux containers without switching the Windows command backYou can execute Linux commands in Windows on your Windows files. You can pipe the output of a Windows command to a Linux program, or pipe Linux output to Windows. In the following example, the Windows winget.exe program generates a list of the current versions (and available update versions) of programs installed in the Windows operating system and the command line pipes this list to the Linux grep command which filters out the lines that contain the word “Git”

Code Block
C:\Users\gilbert>winget list|wsl grep Git
Git                                    Git.Git                                 2.48.1                            winget
GitHub Desktop                         GitHub.GitHubDesktop                    3.4.17                            winget

WSL creates a very special Linux Virtual Machine running the Microsoft version of the Linux Kernel. Unlike other VMs, the Windows system is tightly connected to this Linux VM. Windows programs can directly access the Linux files, Linux has access to the Windows disks, Linux and Windows programs can share the same network adapters, and you can combine Linux and Windows programs on the same command line.

If you want the full Linux Gnome Desktop experience, create a normal Linux VM with Hyper-V. WSL is command line only, but you can install individual Linux GUI programs into WSL and when you run them an application window pops up on the Windows desktop:

...

The Microsoft Linux Kernel runs a special non-Docker container system. You can install one or more Linux “distributions” (Ubuntu, Debian, SUSE, etc.) each into their own container under the Kernel. Unlike Docker, this special container system lets the distributions run systemd background services and shares resources like network adapters. Each distribution has its own isolated file system, processes, and libraries.

The default and probably best adapted distribution is Ubuntu (24.04 LTS). Ubuntu has supported WSL since the earliest version, installs with systemd and snap enabled, and provides the widest library of programs that you can install and run using the apt package manager.

If you are going to run a sequence of Linux commands, then typing the command “wsl” with no arguments converts the Windows command line into a Linux bash session (like running ssh to a Linux machine on the network). Microsoft also provides a bash.exe command that does the same thing.

There is no way to translate Windows file access control to Linux file permissions. The WSL environment is, therefore, private to each Windows user. If two users are logged into the same Windows machine, each has their own personal copy of WSL Ubuntu. The user has implicit full (root) control over his own WSL system and files. Anything in WSL cannot be directly shared with another user. However, if a WSL service connects to the network as port 8080, then other users on the same machine can connect to localhost:8080 and access the service over what appears to be “the network”.

On Windows, the Linux file system in your Ubuntu distribution is referenced by the Windows UNC name of \\wsl$\ubuntu.

Code Block
C:\Users\gilbert>dir \\wsl$\Ubuntu\
Directory of \\wsl$\Ubuntu
03/31/2024  04:00 AM    <DIR>          sbin.usr-is-merged
02/11/2025  07:59 PM         2,424,984 init
09/27/2024  03:13 PM    <DIR>          srv
11/23/2024  08:12 PM    <DIR>          mnt
03/07/2025  11:42 AM    <DIR>          proc
03/07/2025  11:42 AM    <DIR>          dev

In Linux, your Windows disk letters appear as subdirectories of /mnt

Code Block
ls -ltr /mnt
total 0
drwxrwxrwx 1 gilbert gilbert 4096 Mar  7 11:16 d
drwxrwxrwx 1 gilbert gilbert 4096 Mar  7 11:16 c
drwxrwxrwx 1 gilbert gilbert 4096 Mar  7 11:16 e
drwxrwxrwt 2 root    root      60 Mar  7 11:48 wsl
drwxrwxrwt 7 root    root     300 Mar  7 11:48 wslg

While /home/username is your Linux home directory, when you run a Linux program from Windows then the program starts in the same current directory that Windows had, expressed through the mapping:

Code Block
C:\Users\gilbert>wsl
gilbert@MWPFxxxxx:/mnt/c/Users/gilbert$

When you go from Windows to Linux, your Windows Environment variables are merged with Linux environment variables.

In Linux, if you type a command that turns out to be a *.exe program, the Microsoft Linux Kernel handles the transition from Linux back to Windows. At this time there is no way for Windows to automatically recognize a Linux program, so you have to run “wsl <programname>”. However, you can create a programname.bat file somewhere in your Windows path. For example, grep.bat could contain:

wsl grep %*

and then when you run the “grep” command in Windows it will run the Linux program with all the same arguments.

Installation

You can install WSL and the distribution from the Windows Store in Windows 11. However, there is no Store in Windows Server 2025, so you might as well get used to the command line.

Windows 11 Home Edition is not licensed to allow use of the full Hyper-V. There is a limited function “no configuration” version of Hyper-V that gets installed to run WSL.

If WSL is not installed, just run the Windows command

wsl --install

This command installs the minimal Hyper-V feature, then downloads and installs the Microsoft Linux Kernel, and then downloads and installs the default Ubuntu distribution. You typically then have to reboot to enable everything.
To list the other available distributions (without using Store), type the Windows command

wsl --list --online

Some WSL Implementation Internals

Bell Labs created Unix when computers were big expensive machines shared by users. One people switched to using networks of smaller computers, they started work on a successor to Unix with a codname of “Plan 9”. The idea was to make a network of PCs look like a single system with a lot of users. Files on different computers would appear to be one big file system, and programs and servers running on different machines would look like they were all running in one operating system. To make this work, they developed a very powerful network-based disk sharing and program to program communication protocol named “9P”.

Microsoft adopted 9P as a way to combine the Windows OS (disks, files, programs) with a Linux OS so that the combination of two systems looked to the user like a single tightly integrated system. However, the Microsoft adaptation of 9P runs between a logged in Windows user and the WSL Linux VM started by that logged in user.

While Windows and Linux assume that everything is private by default and a user has to explicitly share disks or directories with the network, the 9P model is that everything is shared by default and something is private by setting permissions. This aligns with the view that while you are logged into Windows and running WSL, your Windows programs have full access to everything on your personal copy of a WSL distribution, and WSL programs you run have access to anything on Windows that you (the currently logged in user under which WSL is running) have access to.

WSL originally ran in single user mode (like a Docker container). Then Microsoft added support for systemd by adding an option in the /etc/wsl.conf file in the WSL distribution file system. Ubuntu defaulted to enabling systemd after 23.10.

WSL has some complicated rules for networking that require their own section.

The Distros

Microsoft provides the Hyper-V feature and the Linux Kernel.

Various suppliers provide WSL Distributions in the form of a tar file containing a file system. While the “wsl --install <distroname>” command will download and install a distro from the Microsoft library, you can get a tar file from some other source and create a distro from it.

The non-Docker container that runs a distro stores its files in a Hyper-V virtual disk (*.vhdx) file somewhere in the C:\windows directory. Changes made while the distro runs are written to the virtual disk and persist (whereas Docker containers do not persist changes to their file systems).

Generally, the distro named “Ubuntu” is the same version as the most recent explicit named version (“Ubuntu-24.04” as this is written). You can create two separate versions of the latest version of Ubuntu distinguished by these two names. Each will have their own distinct files on separate virtual disks. If you need a third instance of Ubuntu, you need to “wsl --export” a tar file with a file system and then “wsl --import” the tar file you just generated, giving it a new distro name.

If you are inclined to create a distro from a Beta version of Ubuntu, they keep compressed tar files in a public Web site called https://cloud-images.ubuntu.com/releases/. Remember to unzip the file because “wsl --import” takes a tar and not a tar.gz file.

One distro is designated the Default. It is the distro you run when you type the “wsl” command without a distro name.

WSL Networking

This is a subject that is constantly being changed by Microsoft, but there are a few basic ideas that remain constant.

First, the “localhost” address of 127.0.0.1 behaves like a network adapter but it has nothing to do with the network. In both the Windows and Linux Kernels, localhost is implemented with a dummy device and a special device driver so that a client program can talk to a server program on the same computer without data ever going to any network.

There is support in both the Windows and Linux Kernel drivers for the dummy localhost devices that they know about each other and communicate using Hyper-V to create what amounts to a single coordinated localhost device across both systems. If two programs running in Windows and WSL both try to act as servers on port 8080, then one of them will get the port and the other will be told that the port is in use by someone else. Client programs on either Windows or WSL can then connect to localhost:8080 and talk to whichever server got the port. Data will be transferred as needed between the localhost drivers in the Windows Kernel and the Linux Kernel.

Experimentally (at the time this is being written) this behavior can also be extended to all the real network adapters in the Windows system. If network “mirroring” is enabled, the Linux Kernel creates a dummy Linux network adapter for every real Windows network adapter. It is experimental because Windows has to be in control, so Linux cannot attempt to reconfigure the network adapter to have different addressing or behavior. Client programs on either Windows or Linux can use an adapter to access servers on the real network, and server programs on Windows or Linux get a “first wins” resolution when they try to bind to an IP address and specific port number.

The default WSL network behavior is that the Linux Kernel creates one virtual network adapter that is connected to Windows, and Windows provides a NAT gateway through which client Linux programs in any Linux distribution can access the Internet. Linux server programs should bind to localhost as described above.