WSL and Ubuntu Web Development Setup

I have been using Windows + WSL2 (Ubuntu) for my web development environment for many years now.
Over time I gathered some helpful resources for setting up such development environment, so I decided to write this post to have a reference for myself and you, dear reader!

It shows how to set up

  • WSL and Ubuntu itself
  • git-related tooling
  • prerequisites to be able to install, build, etc. codebases using the Node.js ecosystem
  • pnpm
  • Docker
  • VS Code
  • and, optionally, additional things

Basic setup for WSL #

  1. Open PowerShell as administrator and run:

    wsl --install
    
  2. Restart your machine.

  3. Run "Ubuntu" from the start menu to start an Ubuntu terminal.

  4. It will ask for a username and password - choose some.

  5. I like to not having to enter the password when running things in the WSL distribution, do the following to remove it:

    1. Run:

      sudo visudo
      
    2. At the bottom of the file, add the following line (replace <username> with your chosen username):

      <username> ALL=(ALL) NOPASSWD: ALL
      
    3. Press CTRL+X to exit nano and it will prompt you to save.

    4. Close the terminal and start a new one (by running "Ubuntu" from the start menu).

  6. Update and upgrade all packages:

    sudo apt update && sudo apt upgrade
    

Ensure reliable network connection for WSL #

Sometimes after I have set up WSL it had strange network issues for outbound connections.
I found some GitHub issues regarding that and one of them mentioned a misconfigured network interface.
That's why nowadays I always do the following one time after setting up WSL (based on github.com/microsoft/WSL/issues/7254#issuecomment-1202299443):

  1. Start PowerShell as administrator.

  2. List the network interfaces of Windows:

    netsh interface ipv4 show subinterfaces
    
  3. There should be an interface called "vEthernet (WSL)". Also you must have some WiFi interface, in my case it is called "WiFi". Both interfaces should have the same "MTU" value.
    If not, run this:

    netsh interface ipv4 set subinterface "vEthernet (WSL)" mtu=<MTU-value-of-wifi-interface> store=persistent
    wsl --shutdown
    

Git and GitHub SSH #

  1. Update git to the latest version and add support for git LFS:

    sudo apt-get install git
    sudo apt-get install git-lfs
    git lfs install
    
  2. Configure git:

    git config --global user.name "Your Name"
    git config --global user.email "your_email@example.com"
    
  3. Create a SSH keypair and add the private key to ssh-agent in Ubuntu (based on docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent?platform=linux):

    # Generate a SSH keypair
    # When asked for a passphrase, I typically omit that.
    ssh-keygen -t ed25519 -C "your_email@example.com" -f "~/.ssh/id_rsa_github"
    
    # Start the ssh-agent in the background
    eval "$(ssh-agent -s)"
    
    # Add your SSH private key to the ssh-agent
    ssh-add "~/.ssh/id_rsa_github"
    
    # Configure the key for github.com in the SSH config file
    printf "Host github.com\n    IdentityFile ~/.ssh/id_rsa_github" >> ~/.ssh/config
    
    # copy the public key into Windows clipboard
    cat "~/.ssh/id_rsa_github.pub" | clip.exe
    
  4. Add the public key of your new SSH keypair to your GitHub account (note: you must check out the GitHub repositories via SSH then, not via HTTPS):

    1. Open Add new SSH key of your GitHub settings
    2. Title: I recommend to combine the name of your PC with "WSL", like ZEPHYRUS-M16 WSL.
    3. Key type: Keep Authentication Key
    4. Key: Use the content of the clipboard (= the public key).

Node.js and C/C++ compiler tool chain #

  1. Instead of installing Node.js directly I like to use nvm to be able to have multiple versions of Node.js installed at the same time (you can read more here: pkerschbaum.com/tidbits/multiple-vs-code-instances-with-separate-nodejs-versions).
    Run this to install nvm:

    # replace v0.39.3 by the newest version
    # you can look it up here: https://github.com/nvm-sh/nvm/releases
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
    
  2. To be able to compile npm packages which include native C++ addons, via node-gyp, we need to install a few things:

    sudo apt-get install build-essential g++ libx11-dev libxkbfile-dev libsecret-1-dev python-is-python3
    
  3. npm has a feature to complete commands via the tab key, so let's enable that:

    npm completion >> ~/.bashrc
    
  4. If you want to automatically switch the Node.js version when you enter a directory with a .nvmrc file, you can run the code below in bash to add a hook to your .bashrc:

    cat >> ~/.bashrc <<- EOM
    
    # start: run-nvm-use-if-cwd-has-nvmrc-file
    # run "nvm use" automatically on directory change if a file ".nvmrc" is present in the new directory (https://stackoverflow.com/a/48322289/1700319)
    _nvmrc_hook() {
       if [[ $PWD == $PREV_PWD ]]; then
          return
       fi
    
       PREV_PWD=$PWD
       [[ -f ".nvmrc" ]] && nvm use
    }
    
    if ! [[ "${PROMPT_COMMAND:-}" =~ _nvmrc_hook ]]; then
       PROMPT_COMMAND="_nvmrc_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
    fi
    # end: run-nvm-use-if-cwd-has-nvmrc-file
    EOM
    

pnpm #

  • To install pnpm, follow pnpm.io/installation and pnpm.io/completion.

  • To just use p to run pnpm, add the following to your .bashrc:

    cat >> ~/.bashrc <<- EOM
    
    # start: pnpm-alias
    alias p='pnpm'
    # end: pnpm-alias
    EOM
    

Docker #

Choose one of the following:

Dockerfile linting via hadolint #

  • Install hadolint:

    # replace v2.10.0 by the newest version
    # you can look it up here: https://github.com/hadolint/hadolint/releases
    wget -O /bin/hadolint https://github.com/hadolint/hadolint/releases/download/v2.10.0/hadolint-Linux-x86_64
    sudo chmod 777 /usr/bin/hadolint
    
  • If you use VS Code, install the hadolint extension: marketplace.visualstudio.com/items?itemName=exiasr.hadolint.

Docker multi-architecture builds #

Set up a multi-architecture Docker builder for cross-platform builds:

# from https://unix.stackexchange.com/a/748634
docker buildx create --use --platform=linux/arm64,linux/amd64 --name multi-platform-builder
docker buildx inspect --bootstrap

VS Code #

  • Install the WSL extension, then you can run in an Ubuntu terminal code . to start VS Code in that folder.

    VS Code will inherit all environment variables from the WSL terminal, and nvm uses environment variables to switch Node.js versions.
    As a consequence, VS Code started this way will under-the-hood use the exact version of Node.js you want to use for the project, allowing e.g. git hooks invoked by VS Code to work correctly.

    I use scripts like below to quickly start development:

    alias pkerschbaum_dev='cd ~/workspace/pkerschbaum-homepage && nvm use && code .'
    

Additional things #

  • ngrok is a great tool to expose a local server to the internet (e.g. to open the web application you work on on your mobile phone).

  • To sync the bash history between terminals, run the following in the Ubuntu terminal to add it to your .bashrc:

    cat >> ~/.bashrc <<- EOM
    
    # start: sync-bash-history
    # sync history between terminals, https://subbass.blogspot.com/2009/10/howto-sync-bash-history-between.html
    shopt -s histappend
    PROMPT_COMMAND="history -n; history -a"
    unset HISTFILESIZE
    HISTSIZE=2000
    # end: sync-bash-history
    EOM
    
  • Certain Node.js and Ubuntu combinations can lead to errors when Node.js crypto APIs are used. In case you get an error along the lines error:25066067:DSO support routines:dlfcn_load:could not load the shared library when running Node.js code you can do this: askubuntu.com/questions/1409458/openssl-config-cuases-error-in-node-js-crypto-how-should-the-config-be-updated/1410124#1410124.

  • If there are connection issues (even after fixing the MTU value of the WSL network interface, what we did above) you might need to change the DNS server: github.com/microsoft/WSL/issues/7254#issuecomment-905767204.

  • Some WSL tips:

    • Run explorer.exe . in an Ubuntu terminal to open the Windows Explorer at that directory path.
    • Run echo "hello world" | clip.exe to copy "hello world" to the Windows clipboard.
    • Run wsl --shutdown in Windows CMD or PowerShell to shutdown the WSL instance.