First, execute these commands in Windows pwsh/cmd to ensure the proper features are turned on.
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
Then, update the WSL 2 Linux kernel.
You will want to run wsl --set-default-version 2
so that future distros are set up as SL2. WSL2 or WSL1 refers to the way in which we interact with our distros. WSL2 is the new standard, and will get progressively better as time goes on. Eventually, WSL2 will even have GPU-compute/CUDA support, and full, built-in GUI support. For now, GUI stuff will have to be "hacked" in (see GUI setup) below).
If you already have a WSL1 distro named "Ubuntu", follow the steps for Exporting a distro first, including the final step of unregistering the distro after exporting. Find Ubuntu on the Microsoft Store and install it. The first time you run it (through the Start menu or by running ubuntu
), it will create a WSL2 distro by the name of "Ubuntu". It will ask for a username and password. If you plan to share the distro with others, choose a generic username and password.
In the context of WSL, a "distro" is a specific image/instance of a UNIX-like OS installed in WSL. For instance, the "Ubuntu" distro was automatically created when we ran ubuntu
in console after installing it from the Microsoft Store. To see which distros we have installed, run wsl --list --verbose
or wsl -l -v
for short. If you have Docker installed, you might see some Docker distros in addition to the Ubuntu one automatically created above. Don't worry about those.
Let's say you want to back up the "Ubuntu" distro. We can do that in Windows pwsh/cmd as follows
wsl --export Ubuntu "C:\Users\<You>\Desktop\Ubuntu_backup.tar"
which will pack up the distro into a tarball for later unpacking. We can now safely delete/unregister the "live" distro, now that we have a frozen backup in the tarball. We can do that with wsl --unregister Ubuntu
.
The distro that we just exported can now be re-imported, if so desired. Or else you want to import a different distro that you have previously exported. Maybe it's your old WSL1 distro, and you want to re-import it by a different name. You could do that by running in Windows pwsh/cmd
wsl --import Ubuntu_imported "C:\Users\<You>\WSL\Ubuntu_imported" "C:\Users\<You>\Desktop\Ubuntu_backup.tar"
Let's go through this command one argument at a time.
- The name
Ubuntu_imported
tells WSL that the distro to be imported will be named as such. After importing, you would be able to find it in thewsl -l -v
list by that name. - The path
...\WSL\Ubuntu_imported
is where we want to host/store the "live" distro. If everything goes smoothly, there will be a single VHDX file inside...\WSL\Ubuntu_imported
. This is a virtual hard drive that will hold the entirety of the distro's installation. Windows default distros are installed somewhere in%APPDATA%
, but it's an ugly folder so I prefer importing to a special"WSL"
folder. - The path
"...\Desktop\Ubuntu_backup.tar"
is the source file that we're importing from.
Now if we run wsl -l -v
, we will see Ubuntu_old
as one of the available distros. We can set it as default by entering wsl --set-default Ubuntu_imported
. Now when we type just wsl
in PowerShell, we'll enter the Ubuntu_imported
distro.
By the way, if you SHIFT+Right-click on an item in Windows Explorer, a "Copy as path" option will appear in the context menu, which is great for grabbing paths on the fly.
If a distro doesn't have user.default
set in a /etc/wsl.conf
file, then when it is exported and re-imported, the default user will become root
. In order to fix this, create/modify /etc/wsl.conf
to have the contents of wsl.conf from this Gist. Note that if you're using VSCode with the Remote - WSL extension to attach to the WSL distro, VSCode will not have root privileges and therefore cannot save changes to /etc/wsl.conf
. You will have to use sudo vi /etc/wsl.conf
or sudo nano /etc/wsl.conf
. These are in-terminal text editors. Of the two, nano
is probably more straightforward.
Now you can exit the distro (CTRL+D is a shortcut to log out), and re-open it. You should be greeted by a colorful user@COMPUTER_NAME
prompt instead of a drab root@COMPUTER_NAME
. By the way, if you have the appropriate /etc/wsl.conf
in your distro prior to exporting/re-importing, you won't have to do this every time.
Now that you know how to import, export, and set default distros, you should determine a workflow that suits you. For me, I've found it useful to wsl --export
at "checkpoints" in setting up a distro. For example, I have saved off a barebones Ubuntu install with just Python 3.7 installed. I will probably maintain separate distros for OpenFOAM, OpenCV, and LaTeX. You must choose a distro's name when you wsl --import
it, but you can always change that name down the road by doing a sequence like the following:
wsl --export Distro_with_bad_name "C:\...\Distro.tar"
wsl --unregister Distro_with_bad_name
wsl --import Distro_with_good_name "C:\...\Distro_with_good_name" "C:\...\Distro.tar"
wsl --set-default Distro_with_good_name
If you've already configured /etc/wsl.conf
, you will even be automatically logged in once you spin up the new distro. You can imagine a workflow that involves a couple project-specific distros. You could even send the tarball of a distro to someone to have them be able to run the code you've written. It's kind of like Docker on a budget.
Install VcXSrv. This will run an X Window System on the Windows side, which will be "listening" for events coming from Ubuntu. Then create/modify C:\Program Files\VcXsrv\config.xlaunch
to have the contents of config.xlaunch from this Gist.
Then you will append the contents of .bashrc from this Gist to your own ~/.bashrc
in your Ubuntu distro. Recall, the commands in this file run every time you open an interactive shell/terminal in your Ubuntu install.
Let's say you're working on Python code in ~/projects/homework_1/
, and your Python code has UI elements, like matplotlib
or tkinter
/Qt
buttons and prompts. You can trigger the GUI stuff in ~/.bashrc
to run by creating a file named .env
in your project directory, and putting USE_X=true
(exactly like that, no spaces between equal sign) in the file. It will look like .env from the Gist. Now when an interactive terminal window opens in the project folder, the following block in your .bashrc
will run, triggering the function defined earlier and getting the X Window System up and running.
# Start the X Window System if $USE_X is `true`
# Note that you can't do `if $USE_X` or any simpler variation, due to bash quirks
if [ "$USE_X" = true ]; then
x_start
fi