Run a Dev Container from WSL 2

Posted on
Tags: WSL , docker , devcontainer
Draft: true

Some time ago I watched some interesting videos where developers praised using Visual Studio Code with Dev Containers. And my developer’s heart was beating hard to reach this idea and use it on my system.

I use Windows as my main development system, so I wanted it to run there using all my tools. But it failed. It failed on something I am still trying to figure out. I will mention it later.

The idea

The idea was to have a clean system using docker and Visual Studio Code to create a portable environment, that works everywhere.

Now, after try-and-fail days, as I am not too deep into the topic I went on over a lot of information.

The project

What I wanted to do is have Dev Containers for development and testing. A perfect match for my portfolio page. Because it is the most complete, next to the other projects 😋. Make a guess why.

The portfolio is an Astro.js project, it looked like it would work, as they also have Dev Container example files in their repository on GitHub. I started to test out the waters by getting simple examples to run.

As I am a fan of some alternatives next to the big players, I wanted to try using Podman rather than Docker. So I went with that in mind.

The example projects started fine, and they run as expected, so I just took my portfolio, added the simple Node container with TypeScript, and BOOOM: It failed. More searching and checking for the logs mentioned issues with the node_modules folder and some files being not available. It took too much time to figure out what the problem was.

I switched to Docker with Docker Desktop, but still same issue. My SSD was already tired of all that building process stuff, so it went to the stars 🌠. After some checks, fiddling with examples, GitHub issues, and solved issues (not mine) I considered dumping the idea of Dev Containers.

I tried to check if something was not fine with my project, so I took another example. I created a simple Nuxt project and put it into a Dev Container. It worked. 🤯

At that moment my calm mind switched, grabbed visual changes, walking, playing guitar, and having fun with my family.

Then, I started the Nuxt project again. It stopped working. I got curious, no more confused. The Nuxt project was deleted, but thanks for the hint. Let’s go back to the portfolio.

I checked a lot and some people mentioned some issues with pnpm and Dev Containers.

pnpm tries to set its store on the same volume, but in my case the volume is on another system, so it puts it closest to the path, like on the root of the repository. Then I get a .pnpm-store folder within my repository, and asking myself: why?

I went astray, needed to reset my PC, set it up a new, installed WSL 2 on it, and started the Dev Container from there. It worked. Every single time.

I am not fond of WSL, at least I heard some bad sides for it, but I didn’t want to go too deep into the problems, as at least it worked now for me. The whole thing just exploded my SSD usage by some GB. But nothing installed on Windows 🤣.

The steps

Explanation first: I use SSH for connecting to GitHub or GitLab repositories. It makes my life easy. I also sign now the commits with SSH. So I need it also in my environment I work.

Visual Studio Code could not make it work with a simple setup like: run the Dev Container and everything else is taken care of. No configuration or connection from Windows Git or SSH was reachable, so there were extra steps to make it happen.

Setup WSL

First thing I thought: just map the SSH folder into my WSL instance, done. For now it won’t work, as there are some issues with file rights, so I copy the files directly. Maybe I just put it there for good.

Setup Dev Container

The best part for this is: the file system is compatible from WSL to Dev Container, as both are on Linux. Here the Dev Container can mount the .ssh folder and it works.

Additionally the Dev Container runs the SSH agent and the correct SSH key is added to be used. This works quite nice, even if it needs to be set up like this.

Git also need some configuration. These are on Dev Container side most of the time, but still need some settings. Too bad it could not reach from one source, or I just missed something. Maybe I also map the folders, just like with .ssh?

For Git I included some defaults, so if the creation of the container is missing some configuration, then it will be set, else nothing happens. That is just me being lazy, if I forget something, or just because I am a programmer.

Files

The structure files for my project are set like this:

PROJECT-ROOT
.devcontainer/
| setup-configs/
|	| setup.sh # run all setup files
|	| setup-git-config.sh
|	| setup-ssh-agent.sh
| Dockerfile
| devcontainer.json

Here follows example content on each file.

Dockerfile:

FROM node:22-alpine
ENV HOME="/home/node"
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV ASTRO_TELEMETRY_DISABLED=1

RUN corepack enable && corepack prepare pnpm@latest --activate

# update and install packages
RUN apk update && apk upgrade \
	&& apk add --no-cache git \
	# enable ssh support
	openssh socat

By all means the most important file. I could decide to not use it, and just jump into the Microsoft images that Visual Studio Code offers, but then it would be no fun.

Within this file pnpm is enabled, updates are set and packages for SSH use are installed. Later on I will add some more stuff inside, like Playwright or Cypress.

devcontainer.json

{
	"name": "Portfolio",
	"build": {
		"dockerfile": "Dockerfile",
		// Runs docker from the project root instead of `.devcontainer`,
		// to ensure `COPY pnpm-lock.yaml ./` works
		"context": ".."
	},
	"mounts": [
		"source=/home/<your-wsl-user>/.ssh,target=/home/node/.ssh,type=bind,consistency=cached"
	],
	"remoteEnv": {
		"SSH_AUTH_SOCK": "/tmp/ssh-agent.sock"
	},
	"postCreateCommand": "chmod +x ./.devcontainer/setup-configs/setup.sh && ./.devcontainer/setup-configs/setup.sh",
	"waitFor": "postCreateCommand",
	"postAttachCommand": {
		"Astro dev": "pnpm run dev"
	},
	"forwardPorts": [4321],
	"remoteUser": "node",
	"customizations": {
		"vscode": {
			"extensions": ["astro-build.astro-vscode", "esbenp.prettier-vscode"]
		}
	}
}

This file is the heart of all Dev Container environments. What does it do?

setup.sh:

#!/bin/sh

cd ./.devcontainer/setup-configs

chmod +x ./setup-ssh-agent.sh
./setup-ssh-agent.sh

chmod +x ./setup-git-config.sh
./setup-git-config.sh

pnpm install && pnpm run build

setup-ssh-agent.sh:

#!/bin/sh

SOCKET=/tmp/ssh-agent.sock

# Remove existing socket file if it exists
if [ -e $SOCKET ]; then
  rm -f $SOCKET
fi

# Start the agent
eval $(ssh-agent -a $SOCKET)

# Start socat to forward the SSH agent
socat UNIX-LISTEN:$SOCKET,fork EXEC:'ssh-agent -a $SOCKET' &

setup-git-config.sh:

#!/bin/sh

# Function to check and set Git configuration
check_and_set_git_config() {
  local key=$1
  local value=$2

  if git config --global --get "$key" > /dev/null; then
    echo "$key is already set."
  else
    echo "Setting $key to $value."
    git config --global "$key" "$value"
  fi
}

# Check and set Git configuration options
check_and_set_git_config "user.signingkey" "/home/node/.ssh/id_ed25519"
check_and_set_git_config "commit.gpgsign" "false"
check_and_set_git_config "gpg.format" "ssh"
check_and_set_git_config "gpg.ssh.allowedSignersFile" "/home/node/.ssh/allowed_signers"

Open points

Podman. This thing should run in a pod, not in a Docker container. Because I want it like that. WSL needs to be set up for that, as it has some issues to figure out where the executable file is (on Windows). I need a socket connection, and all that stuff is right now not on the plan, so Docker it is, for productivity …

Conclusion

The issue with the file system was a total throw over. I would move on to WSL, but I also want to stay in Windows without that one layer between the Dev Container and the host operating system.

I take the bitter pill in this case. Switching to Linux is not an option right now. This way I can still stay clean with my Windows installations, as I just need an editor, WSL and Docker to run most of the things. Anything else is sandboxed configuration, out of my main system.