How to Configure a WSL2 Development Workspace: Ubuntu 20.04, Node.js, MongoDB, VS Code, and Docker

How to Configure a WSL2 Development Workspace: Ubuntu 20.04, Node.js, MongoDB, VS Code, and Docker

Previously, I wrote an article about setting up Windows 10 and Pop!_OS 20.04 on my system. I went on a rant and thought a "dual boot" setup was best for me—I was wrong.

I have since then set up WSL2 on Windows 10 with Ubuntu 20.04 (Focal Fossa) and a stack that includes:

  • Docker Desktop for Windows with WSL2 as a Backend.

  • Node.js

  • MongoDB, MySQL, Redis, and PostgreSQL

  • NGINX

  • VS Code, Vim, etc.

This setup works like a charm for my needs, and I am hopeful it will work for you, too.

Here is the story.

Let's start with a quick segue to understanding WSL a bit. Fair warning, this is an oversimplification of the technical docs; for your benefit:

What is WSL?

Windows Subsystem for Linux (WSL) is a Windows 10 feature that lets you use a GNU/Linux environment on top of Windows.

Yes, that is right. It works like any other app on Windows.

Why is it any better than a VM?

It's faster to launch and straightforward to set up vs. using VirtualBox or other solutions.

I have seen discussions about WSL1 & WSL2 in the wild. What's the difference?

WSL comes in two flavors, versions 1 and 2.

WSL1 is an abstraction with no virtualization and direct access to some system hardware.

I am over-simplifying the explanation here, but It attempts to map system calls from GNU/Linux to their Windows equivalents. It's not the same Linux kernel, but it's a close match.

It is just enough "shim" for both worlds to work together.

Think of it as conceptually similar to an interpreter between two leaders from different countries who don't speak a common language.

WSL2 is a lightweight "Virtual Machine" without the cruft.

It's a new architecture that uses a subset of the Hyper-V technology to enable a certain degree of virtualization.

It is like a virtual machine that runs as a native app on Windows, with its networking and access to most system hardware.

Most notably, WSL2 ships a "Full Linux Kernel" based on the same Linux kernel that powers billions of devices worldwide.

WSL2 also lets you mount additional file systems.

If you have a multi-boot setup, you can access the file system of any other OS by mounting its partition within WSL2.

How cool is that?

**Note: ** The WSL2 kernel ships with a few caveats, which we will discuss later.

Which version should I use?

Both WSL1 and WSL2 have their real-world applications. Here is a comparison of use cases for both.

Use WSL1 to "share" source code and files between Windows and GNU/Linux. WSL1 is faster with cross-system file access.

Use WSL2 if you are developing for end targets like servers (which often run on GNU/Linux). This means that if you need a GNU/Unix dev environment without needing to share anything on the Windows host, WSL2 is a good option.

Hosting a SaaS application is an example of this use case.

The Windows file system is accessible via WSL in both versions. The general recommendation is to keep project files on GNU/Linux if you use WSL2.

Note: Most Windows 10 editions, including Windows 10 Home, is eligible for installing WSL2 and Docker Desktop.

Setting up the development environment

OK - now that we know what WSL is, let's install it, specifically WSL2 and a dev-stack including Node.js, MongoDB, etc., and more.

I recommend you start by installing the Windows Terminal. This part is optional, of course, and you can skip installing WSL.

Windows 10 ships with CMD prompt and PowerShell. You can also install a third-party terminal emulator like Cmder - born out of frustration and lack of a fast, customizable terminal on Windows in the past years.

But this is changing.

The new Windows Terminal is a generation leap over the past offerings and can potentially become the iTerm2 of the Windows ecosystem. I like it and plan to write a detailed post covering it.

In an excellent drop-down menu, it should automatically add entries for the shells installed on Windows. Later, when you install WSL2 with a distro, it will add entries for those, too.

Quite nifty.

image.png

Installing Windows Subsystem for Linux (WSL2)

Follow the official documentation for installing WSL2 and confirm your Windows version meets the criteria for using WSL2 available in the documentation.

Here is broadly what you need to do:

  1. Update to the latest Windows 10 (May 2020, as of this post).

  2. Enable Windows Subsystem for Linux and Virtual Machine Platform features in Programs and Features. You can either:

    Open PowerShell as Administrator and run:

     dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
     dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
    

    OR

    Open Windows Search (Win ⊞ + S) -> Enter "Turn Windows features on or off" to launch the Windows Features window. Scroll and check these boxes:

     [x] Virtual Machine Platform
     [x] Windows Subsystem for Linux
    

    image.png

  3. Restart your system.

  4. Update the kernel, and set WSL2 as the default version.

    Open PowerShell as Administrator and run:

     wsl --set-default-version 2
    

    If you get warnings about updating the kernel, visit the link in the message and follow the instructions.

Installing a Linux distribution

Currently, you can only install Linux distributions from the Windows Store. It has the most popular distros, including Ubuntu, Fedora, Debian, etc.

While there are ways to install distros unavailable on the Windows Store, it's pretty involving. You would probably need to package distros as an application for WSL2. If that's your thing, then StackOverflow and SuperUser are your friends.

For our purposes, we will be installing Ubuntu 20.04.

  1. Go ahead and get Ubuntu 20.04 from the Windows Store. It takes a few clicks and installs as an app on the start menu.

  2. Launch and start the Ubuntu 20.04 application.

    image.png

  3. You should see a pre-startup screen:

    Set up a new username and password (these have nothing to do with your Windows account).

    image.png

    Update the packages:

     sudo apt update
     sudo apt upgrade -y
    
     # cleanup
     sudo apt autoremove -y
    
  4. Once you have completed the one-time setup, you can either use Ubuntu as a standalone application or launch Ubuntu within your favorite terminal app with the WSL2(wsl.exe) executable.

Now, I configure my dotfiles with the fantastic Starship prompt for ease.

image.png

Feel free to do that if you have some, or skip to the following parts.

Installing Docker Desktop for Windows

We also need databases and services as a part of our stack. This step typically means installing services like MongoDB, MySQL, PostgreSQL, NGINX, etc.

Let's use Docker Desktop for Windows with WSL2 as the backend for these services.

A backend, in this case, means we can configure Docker for running containers on either WSL2 or Windows as a host as needed.

Buy one and get another for free. Both are free? Bad pun intended.

Why Docker? Can't we install apps natively on Windows or WSL2?

Yes, you can. It's not the best experience.

Here is my reasoning:

Reason 1:

Version management of apps used as services is relatively easy with docker containers. For example, it's a great way to run multiple versions of MongoDB.

Reason 2:

You can access applications running on WSL2 from Windows with localhost URLs. For instance, a node web app running on the port 3000 on the WSL2 side is available at https://localhost:3000 on the Windows side.

The reverse is a pain.

If you have an app running on Windows at some arbitrary port, it is not trivial to reach it from WSL2. If you installed MongoDB or MySQL on Windows 10, making your node app hit the DB's endpoints is a chore.

One workaround I found on the issue thread uses forwarding connections. A utility like socat can pair a given <PORT_NO> on Windows to another on WSL2:

socat TCP-LISTEN:<PORT_NO>,fork EXEC:"/mnt/c/Windows/System32/wsl.exe -d ubuntuwsl2 socat - TCP\:localhost\:<PORT_NO>"

This is a deal-breaker for installing services on the Windows side.

Similarly, the WSL2 instance gets its own IP address separate from the Windows each time it's booted up and gets set in the /etc/resolve.conf

You would use something like this to retrieve the IP if you need it instead of using localhost as I mentioned earlier.

cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'

image.png

Reason 3:

I mentioned a caveat with the Linux kernel that ships with WSL2. It does not use the systemd utility commonly used by many apps to run as a service natively.

Instead, you need to use init.d to startup services at boot.

Here is a StackOverflow discussion on configuring init.d if you are feeling adventurous.

Docker a natural choice for running containerized applications and fixes these shortcomings of setting up service apps.

Steps to install Docker on Windows and configure WSL2 as a backend.

Follow the official documentation to install your Windows edition's docker desktop.

Here is broadly what you need to do:

  1. Download and install Docker for Desktop, available for Windows 10 Pro and Home.

    image.png

    You may need a restart/sign-out to get it working.

  2. Go to Settings -> General -> Enable the Use the WSL2-based engine.

    image.png

  3. Go to Settings -> Resources -> WSL Integration

    • Make sure the "Enable integration with my default WSL distro" is selected

    • The toggle for all the distros you want the integration to be active on is flipped to ON.

image.png

  1. Close and launch the terminal and add your user to the Docker UserGroup.

     sudo usermod -aG docker raisedadead
    

    This step should let you execute docker commands with ease.

Limit resource usage

One of the side-effects of using Docker for Windows with WSL2 as the backend is high system memory use.

image.png

The Vmmem is hitting 7 GBs for no reason. That's quite a lot, even on my machine, which has 32 GB of memory.

Here is how you can limit the memory usage on WSL2:

  1. Create a .wslconfig

    Go to your user profile path on Windows Explorer and create the .wslconfig file with the following contents:

     [wsl2]
     memory=6GB # Limits VM memory in WSL 2 to 6 GB
    

    You can check your user profile path in PowerShell like so:

      echo $env:USERPROFILE
    

    I created the file at C:\Users\raisedadead\.wslconfig

  2. Open PowerShell and shutdown WSL:

     wsl.exe --shutdown
    
  3. Docker will prompt you with a warning.

    image.png

    Go ahead and click Restart.

    Memory footprint should be smaller this time:

    image.png

Here are the detailed configuration options. Go ahead and tweak additional resources if you prefer.

Set up Apps, Tools, and Services for the dev-stack

Now that we have a basic Ubuntu 20.04 on WSL2 with Docker Desktop for Windows integrated let's set up the rest:

  1. Install a language runtime.

    We will install Node.js, but the steps are similar for Python, Ruby, Rust, etc.

  2. Install a database as a service running in the background.

    We will install MongoDB, but the steps are similar for Redis, PostgreSQL, etc.

  3. Set up a code editor like VS Code and Vim.

Install Node.js

I use the popular nvm - node version manager to manage Node.js versions. It even lets you switch versions by using a .nvmrc file in your project repo.

Visit the repo page on GitHub and run the script to install nvm.

image.png

Once installed, the script will give you further instructions to add the nvm utility to the Path. Go ahead and do that for your shell.

image.png

Next, install the latest LTS version of Node.js.

nvm install --lots

Or get any version, and nvm will resolve the latest matching SemVer:

# nvm install <version>
nvm install 13

It should only take a few minutes, and you will have a functional Node.js setup. You can use any version of Node.js binaries you have installed, like so:

# nvm use <version>
nvm use 12

Install MongoDB

Running docker containers is quite neat. Follow the official docs if you need a deeper understanding of Docker.

Here is a quick set of commands to pull and run MongoDB 3.6.20 from Docker Hub.

docker pull mongo:3
docker run -it -v mongodata:/data/db -p 27017:27017 --name mongodb --restart unless-stopped -d mongo:3

Voila, you should have MongoDB running on port 27017 and accessible via the localhost URL on both Windows and Ubuntu 20.04.

Installing additional services is similar to the above. You would pull the corresponding benefits from the docker hub and run them at the needed ports.

Install a code editor

I like using both VS Code and Vim interchangeably.

The VS Code integration for WSL2 is pretty darn good. It blurs the lines between your editing environment and the development environment quite well.

You can navigate to your project path in Ubuntu and type:

code .

The alias command code launches VS Code on the Windows side, automatically installing and configuring a WSL2 extension.

Pretty neat.

Here is what you need to do:

  1. Install Visual Studio Code.

  2. Launch Ubuntu 20.04 via Windows Terminal or its standalone app.

  3. Navigate to your project folder and type in the commands:

      cd ~/DEV/portfolio
      code .
    

    You will notice the terminal within VS Code works nicely as well.

  4. Optionally, if you prefer to use Vim or nano you can get them from apt:

     sudo apt install vim
    

I can then configure my favorite extensions and themes for my workflows.

Parting thoughts

I have been using this setup for a few days now. Baring a few gotcha's, it has been quite a rewarding experience.

I have noticed the difference in speed. WSL2 does deliver on its promise vs. a traditional VM.

The workflow is pretty smooth.

This is the ideal setup if you want to switch from macOS or use Windows as a daily driver with a GNU/Linux target for delivery. The WSL2 project is evolving, and there are loads of improvements planned.

I will explore installing an X-Server to run GUI apps in WSL2 next and see what that feels like.

I hope you liked this yet another long post. If you do and have any feedback, give me a shoutout on Twitter.

While you are there, give me a follow.

Stay safe and stay home. We got this.

Did you find this article valuable?

Support Mrugesh Mohapatra by becoming a sponsor. Any amount is appreciated!