A WSL2 Dev Setup with Ubuntu 20.04, Node.js, MongoDB, VS Code and Docker

Subscribe to my newsletter and never miss my upcoming articles

A few days ago, I wrote an article, about setting up Windows 10 and Pop!_OS 20.04 on my system.

I went on with this rant:

Dual boot is easier and makes sense for me.

Turns out, I also setup WSL2 on Windows 10 with Ubuntu 20.04 (Focal Fossa), and a dev-stack that includes:

  • Docker Desktop for Windows with WSL2 as a Backend.
  • Node.js
  • MongoDB, MySQL, Redis and PostgreSQL
  • NGINX
  • VS Code, Vim, etc.

I liked the idea of testing my skills for configuring such a setup. It gives quick me access to projects for demos, PR reviews, etc. when I am on Windows.

Here is the story.

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

What is WSL?

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

Yes, that is right.

Follow along this dev-log and you will be able to run Ubuntu 20.04 like any another app on Windows.

Why is it any better than a VM?

It's fast. Quite fast, and straight to set up.

OK - I have seen WSL1 & WSL2 being discussed in the wild? What's the difference?

WSL comes in two flavors, version 1 and 2.

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

It's an attempt to map system calls from GNU/Linux to their Windows equivalents. It's not the same Linux kernel, but it's a very close match.

Enough "shim" for both the worlds to work together.

Think of it being 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 which uses a subset of the Hyper-V technology, to enable a certain degree of virtualization.

It's literally a VM, that happens to be running like a native app on Windows, with its own 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.

Meaning, 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 comes with some caveats we will discuss later.

Which version should I use?

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

Use WSL1 if you need 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). Meaning 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. It is recommended to keep project files on GNU/Linux side if you are going to be using 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 get going with installing it, specifically WSL2 and a dev-stack including Node.js, MongoDB, etc. and more.

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

Windows 10 ships with CMD 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 has the potential of becoming the iTerm2 of Windows ecosystem. I am liking it quite a lot and plan to write a post covering it in detail.

It should automatically add entries for the shells installed on Windows, in a nice drop-down menu. 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 release of 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
    

    You may get warnings about updating the kernel, this is expected. Visit the link in the message and follow the instructions.

Installing a Linux distribution

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

While there are ways to install distros not available on the Windows Store, it's quite 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's 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 are done with 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.

At this point, I configure my dotfiles with the awesome Starship prompt for ease.

image.png

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

Installing Docker Desktop for Windows

We also need databases and services as a part our dev-stack. This 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. Except both are free? Bad pun intended.

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

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

Here is my reasoning:

Reason 1:

Version management of apps used as services is quite easy with docker containers. It's a great way to run multiple versions of MongoDB for example.

Reason 2:

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

The reverse is a pain.

That is 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, it is quite a chore to make your node app to hit the DB's endpoints.

One work around 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 deal breaker for installing services on Windows side.

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

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

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

image.png

Reason 3:

I mentioned there is 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 going about configuring init.d if you are feeling adventurous.

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

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

Follow the official documentation for installing docker desktop for your Windows edition.

Here is broadly what you need to do:

  1. Download and install Docker for Desktop, available for both 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 that you want the integration to be active on are flipped to ON.

      image.png

  4. Close and launch the terminal (in my case that is Ubuntu-20.04 via Windows Terminal) and add your user to the docker UserGroup.

     sudo usermod -aG docker raisedadead
    

    This 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 the memory consumption.

image.png

The Vmmem is hitting 7 GBs for no reason. That's quite a lot, even on my machine, which has a decent 32 GBs 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 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 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's 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 it to the Path. Go ahead and do that for your shell.

image.png

Next install the latest LTS (or your preferred) version of Node.js

nvm install --lts

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

# nvm install <version>
nvm install 13

It should only take a couple 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 service from 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 simply navigate to your project path in Ubuntu and type:

code .

The alias command code launches VS Code on Windows side, automatically installs and configures 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 would 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 am 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.

If you are looking to switch from macOS or use Windows as a daily driver with a GNU/Linux target for delivery, this is the ideal setup. The WSL2 project is evolving and there are loads of improvements coming our way.

I will be exploring installing a X-Server to run GUI apps in WSL2 next and see how that feels like.

Hope you liked this yet another long dev-log. If you did or 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.

No Comments Yet