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 Unix “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. Without background daemons started automatically at boot time, there is no sshd to login through the network or snapd to manage snap packaged programs. This is not a technical problem with WSL 2, but may currently be enforced so there is one Ubuntu distribution that runs in both the old WSL 1 (where daemons are impossible) and the new WSL 2 (where daemons are technically possible but are disabled and are not easy to enable). Other distributions may start systemd and then enforce other rules.
WSL has only a subset of the packages that are available to a real Ubuntu installed in a VM. You may be familiar with the Ubuntu practice of suggesting to you a package to install when you type a command that is not currently installed:
Code Block |
---|
gilbert@wholewheat:/mnt/c/Users/gilbert$ xclock
Command 'xclock' not found, but can be installed with:
sudo apt install x11-apps |
This message will not appear for commands normally installed as snaps or packages that install daemons. Also, they modify the message when you enter a Docker command, because they then tell you to install Docker Desktop for Windows on the Windows system instead of installing docker directly into WSL Linux.
WSL Graphics Applications
WSL supports simple Linux X11 GUI programs. You need a X11 terminal emulator on your Windows system to display the output and can install X-Win32 from the Yale Software Library. Then in a WSL bash.exe command prompt session to WSL, you set the $DISPLAY environment variable to point to the IP address that the Windows system uses to connect to the real network, and then run an X Window program like “xclock”. A Linux clock window pops up in Windows.
However, other ways to run Linux on Windows don’t require X11. For example, if you install Ubuntu in a Hyper-V “Quick Create” VM, you get a whole Gnome desktop with the Hyper-V version of Remote Desktop. The trick here is that Hyper-V (and VirtualBox) have the ability to generate what the VM sees as a virtual video adapter card. In the VM, the Linux OS renders X11 output on what it thinks is the hardware monitor connected to the computer, and Hyper-V converts that screen image into Remote Desktop Protocol.
X11 has always supported remote access, but it was designed for older hardware and slower networks and is hard to integrate with Windows. There is a newer alternative called Wayland, and its display manager is called Weston. Microsoft prefers it because it already has support for RDP, although they found it useful to add additional features and contribute the changes back to open source. One important feature is the ability to remote the individual application windows instead of an entire screen image. When the remote protocol is transmitted over a network, this is called RAIL. When it is transmitted between Hyper-V VMs, it is called VAIL.
WSL is not intended to run an image of a Windows screen and an image of a Gnome desktop side by side. It wants to create a single integrated environment with a mixture of Windows and Linux applications. Being Microsoft, they put the Windows Explorer desktop in control. This is called WSLG. It was described by Microsoft in the Sept 2020 XDC sessions and will be released to Windows Insiders soon.
Since the enhancements to Weston are contributed, some of these features should eventually appear in real Linux VMs, particularly those found in the Cloud.
Applications that are supposed to be represented by icons on the Gnome desktop screen (like Firefox) also install a .desktop file in particular directories. Here is the twist. At some point, with details still to be worked out, Windows and WSL will discover the list of programs with .desktop files and will add them to the Windows Start menu as shortcuts to the Linux executable. You can click on the program in Windows, and launch the program in WSL Linux, but have the application window appear on the Windows desktop next to all your Windows apps. Later on, we will see that the CLI is similarly integrated, so you can run Linux programs using Command Prompt or Powershell while running Windows programs from Bash.
Some WSL Implementation Internals
The magic that makes this work is a protocol developed by Bell Labs as part of a new OS that would be a successor to Unix (though it turned out that nobody wanted to replace Unix). “Plan 9” was supposed to be a single OS that ran across many computers. To make that work, they created the “9P” protocol that combined file sharing, starting a program on another machine, and piping data from one machine to another. WSL uses a version of “9P” so that programs on Windows can start programs on Linux, Linux programs can start Windows programs, and the files of both systems are visible to each other.
User mode programs talk across systems using 9P. However, to be most efficient, Microsoft created modifications to the Linux Kernel so that it can talk to the Windows Kernel directly using the Hyper-V virtual bus. Virtual Machines start out by simulating the hardware of a disk controller chip or LAN chip. Eventually they add logic to their Kernel so that when it detects that it is running on a virtual machine, it communicates data and makes requests at a higher level than chip emulation. Some support for various virtual machine supervisors, including Hyper-V, was already in the Linux Kernel, but Microsoft is adding more.
For example, disk I/O goes through a series of layers. The file request is sent to the file system which translates it into a set of block numbers in a partition. That is then translated to commands for a SATA or SCSI disk controller. In a VM, the Kernel should stop after the file system is done and send the relative block numbers to the Windows host Kernel. The host Kernel can then translated the block number, possibly into an offset in a VHDX disk image file and eventually into real block numbers on a real disk. Emulating SATA or SCSI controller chip commands is a waste of time. Windows doesn’t have or need an ext4 file system driver because the WSL Linux Kernel has one. So Microsoft is distributing to testers a version of Windows that uses the WSL 2 Kernel to mount Linux disks and make them available to the Windows system.
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. No stop the VM and take a snapshot of its memory. On a real machine the OS has to check for changes in the hardware (you might have added a disk, or changed adapter cards). However, you can arrange for all the VMs to be identical. Then to start WSL up, you don’t have to repeat the BIOS/Grub/Kernel/Driver load sequence. You just restore the original snapshot of the kernel into virtual memory, and then start it.
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.