• Quick intro to VS Code DevContainer: It’s a feature of vscode that enables you to launch your development environment from the docker container. In other words it looks like you have opened a regular vscode IDE, but your code and file system you are browsing are from a docker container. To know more read Developing inside a container
  • Quick intro of WSL2: WSL(Windows Subsystem for Linux2) is for anyone who likes bash and linux environment but having the Host OS as Windows. Unlike VM’s like Oracle VirtualBox which eats up memory, WSL2 is highly performant and light weight. We can access both the file systems i.e., windows and linux from the same WSL prompt. To know more about WSL2 read WSL Documentation

Here is how my dev environment setup looks like

Host OS: Windows

Docker Desktop : Installed on Windows with WSL2 integration - Meaning docker data is on WSL2 file system

WSL2: Having Debian as default distro WSL2 -l -v command output

VS Code: Installed on Windows. Acts as VSCode Client

Git: Installed in WSL2

Vs Code server: On WSL2. For the first time on WSL2 prompt when we do code --version it will automatically download the vscode server and required extensions.

Dev Repos: All my code base and repos are on WSL2 file system

SSH keys: On WSL2

Here are my usual steps to launch project in dev container

  1. Open project from WSL file system in vscode

Open a project from WSL file system in vscode

  1. And then Reopen in Container since I have enabled my project with VsCode Dev container feature ready having required configs and setup under .devcontainer/devcontainer.json.

Reopen in container popup

  1. After that it will open the code from vscode dev container like you are browsing code from a remote system

Reopen in container popup

  1. Now, we are working from vs code dev container and see that the container is in running state

Reopen in container popup

  1. Finally, start working from the dev container. Note that I have bind mounted the workspace from WSL2 to dev container so that changes remain on WSL2 file system even after the container is exited/stopped or removed/deleted.

But, to reach this state I have faced quite a few roadblocks. The relevant roadblocks for this post are

  • Problem: VsCode is not responsive when opened the project in dev container
    • Fix:
      • This was fixed after switching my docker conatiner from windows containers to WSL2 Linux containers.
      • That’s not enough, I had to tweak wsl configurations by creating .wslconfig in my windows home directory i.e., C:\Users\<username>\.wslconfig. Here is the sample .wslconfig configurations.

        # Read: https://learn.microsoft.com/en-us/windows/wsl/wsl-config#configure-global-options-with-wslconfig
        # Settings apply across all Linux distros running on WSL 2
        [wsl2]
        # 50% of total memory on Windows or 8GB, whichever is less; on builds before 20175: 80% of your total memory on Windows
        memory=16GB # I am running on 32 GB RAM
        # The same number of logical processors on Windows: https://learn.microsoft.com/en-us/windows/wsl/wsl-config#configure-global-options-with-wslconfig
        processors=8
        # Sets amount of swap storage space to 8GB, default is 25% of available RAM
        swap=30GB # I put it like this to experiment
        
  • Problem: My Vs Code Java Language server is too slow
    • Fix:
      • Added the following props in .devcontainer.json

        • "java.memory.maximum": "4g",
          "java.signatureHelp.enabled": false,
          "java.jdt.ls.vmargs": "-Xms2G -Xmx2G -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true",
          
  • Problem: ssh-agent is started everytime wsl is restarted, meaning all the identities added perviously are deleted. Unlike Windows ssh-agent it doesn’t save/remember your identities even after system restart.
    • Fix:
      • Add a script in .profile to add all the identites at the startup of WSL process
      • Here is the how my complete .profile shell script looks like

        # if running bash
        if [ -n "$BASH_VERSION" ]; then
            # include .bashrc if it exists
            if [ -f "$HOME/.bashrc" ]; then
                . "$HOME/.bashrc"
            fi
        fi
        
        # set PATH so it includes user's private bin if it exists
        if [ -d "$HOME/bin" ] ; then
            PATH="$HOME/bin:$PATH"
        fi
        
        # set PATH so it includes user's private bin if it exists
        if [ -d "$HOME/.local/bin" ] ; then
            PATH="$HOME/.local/bin:$PATH"
        fi
        
        # @sairaghava adding new lines to start ssh-agent in startup of WSL2 and add all the identities to ssh-agent
        if [ -z "$SSH_AUTH_SOCK" ]; then
          echo "sairaghavak: SSH_AUTH_SOCK is always empty during WSL prompt startup, during that time no env_vars of WSL are visible and are all empty. That's why SSH_AUTH_SOCK is empty, .profile gets executed everytime you to try to open a new WSL session"
          # Check for a currently running instance of the agent
          RUNNING_AGENT="`ps -ax | grep 'ssh-agent -s' | grep -v grep | wc -l | tr -d '[:space:]'`"
          if [ "$RUNNING_AGENT" = "0" ]; then
                # Launch a new instance of the agent
                echo "There is no running SSH agent, so launching a new ssh-agent"
                ssh-agent -s &> $HOME/.ssh/ssh-agent
                echo "New ssh-agent started its PID = $(pgrep ssh-agent)"
          fi
        
          eval `cat $HOME/.ssh/ssh-agent`
          echo "sairaghavak: SSH-Agent PID = $(pgrep ssh-agent)"
        
          # @sairaghava: New code to execute ssh-add commnad only at the startup of WSL2, but not everytime you get into WSL prompt
          sshAddList=$(ssh-add -l | grep "The agent has no identities.")
          echo "$sshAddList"
          # No space allowed while doing variable assignment in shell script
          if [[ "$sshAddList" == "The agent has no identities." ]]; then
                echo "Looks like you are starting WSL2 i.e, it's status is in stopped state, you are trying to bring it to running state"
                echo "As ssh-add -l has no identities, authenticate once for all your private keys"
                if [ -t 1 ]; then
                    ssh-add ~/.ssh/srk/github/id_ed25519
                    ssh-add ~/.ssh/srk/gitlab/id_ed25519
                fi
          fi
          # Note: Once ssh-add is executed successfully for the first time. As I am bind mounting .ssh folder from WSL2 to VsCode devcontainer all keys are visible in dev container as well
        fi
        
      • Given wsl is in stopped state WSL in stopped state
      • After adding the above script in .profile, every time I start WSL with command wsl, it will ask for ssh private key passwords as shown in the below image. WSL Startup actions
      • When WSL is already running, but the WSL prompt is closed and reopened then this is how it echoes because of my echo statements in shell script. WSL Startup actions
      • Wish ssh-agent in WSL2/Linux stores the identities like shh-agent in windows does even after system restarts.
  • Problem: Could not do git commit, push from the dev container
    • Fix:
      • Bind mounted the .ssh folder from WSL2 to Dev container
      • Update my docker-compose.yml with the following config

          - type: bind
            source: ~/.ssh # folder in host. If you are launching vscode from wsl2 host is wsl2, otherwise if vscode is launched from windows host is windows
            target: /home/<same-as-remoteUser-value-mentioned-in-devcontainer.json>/.ssh # folder in vscode dev container # target must be an absolute path not ~/.ssh otherwise it will throw error
            read_only: true
        
      • After this update to docker-compose.yml do a rebuild of dev container, then the .ssh folder is mounted inside the vscode dev container and observed that all the SSH keys available in WSL2’s ~/.ssh folder are also available inside dev container
        • Verified it using ssh-add -l on WLS2 and vs code dev container wherein the output is same.
      • Also authenticated to various hosts defined in ~/.ssh/config in WSL2.
        • Let’s say ssh -T git@srk-github works from vs code dev container and of course from WSL2.
        • Another example is ssh -T git@srk-gitlab also works from within the dev container and WSL2.
        • It’s working because the vs code dev container also has ssh program/utility installed in /usr/bin/ssh and I am just bind mounting the .ssh folder from WSL2 to vs code dev container so that ~/.ssh in dev container has got all required keys and config stuff to do ssh authentication and other operations.

This bind mounting of .ssh folder can be applied in other similar setups like when the Host OS is linux say Ubuntu/Linux Mint, and you are launching VsCode dev container, you can bind mount .ssh folder from host to dev container which empowers your git, ssh operations to work smoothly.

In summary, have the SSH keys setup in WSL2 file system and just do a bind mount of .ssh folder into vscode dev container by updating docker-compose.yml volumes section. So that the whole of ~/.ssh in WSL2 is visible and mirrored as readonly inside dev container, and ssh util in dev container can operate with data in ~/.ssh of dev container. Note that in this kind of setup, we don’t need SSH or git to be setup on Windows, but they should be on WSL2.

References:

  1. Docker Architecture
  2. VSCode Remote development from DevContainers
  3. WSL2