Getting started with VS Code Remote Containers on Windows
I've been trying out VS Code Remote Containers recently and I'm honestly really impressed with the experience so far. After some (thankfully really well documented) initial setup I am able to use remote containers to quickly setup a development environment that is bootstrapped with the tools, SDKs and runtimes required to work with a variety of different technology stacks, and without having to install and configure any of those things directly on my local machine.
There's alot of potential here and the crossover with GitHub Codespaces surely guarantees this way of working will become mainstream in the not too distant future. I thought I would share my motivations for wanting to try remote containers, and describe how I got up and running. I'm also planning on turning this into a mini series of posts to cover related topics such as personalising your remote containers and using GitHub Codespaces.
Motivation
I have always done my development work on a Windows machine. Historically the bulk of the development projects I have worked on involved Microsoft's .NET Framework so it was the only real choice. Sometimes my development environment was on a Windows virtual machine, and I ocassionally hosted a Windows VM on OSX, but for the longest time I have just installed my development tools directly on to my Windows machine.
I don't see that changing any time soon - I like Windows and I still develop on projects at work that use the .NET Framework and installing development tools directly on the host OS continues to be the simplest setup (for me) in that case - but as I have started to branch out a little into working on largely JavaScript and Node based projects and started to work more with Open Source projects (both as a contributor and maintainer) I started to wonder what tools were out there that would allow me to:
make my development environment more portable so it would be easier for people to contribute to my projects; and
allow me to more easily configure my development environment to meet particular requirements when switching to one project or another
This is of course not a new problem, and I'm aware of and tried out tools in the past such as Vagrant and Docker (and of course plain old virtual machines), but I've always eventually given up and felt limited by support for Windows and .NET Framework, and I never really did get far enough in my exploration to try out a development workflow using these tools on a "real world" project.
But, as is the way with technology, things have progressed a lot in the past few years so I thought I would do a little bit of research as to what new or improved options there might be in this space - I had read that WSL2 support in Docker Desktop improved the Docker experience on Windows and also that VS Code had some good extensions for remote development via WSL (Windows Subsystem for Linux) - so I thought I would look into WSL and maybe give Docker another go.
Heading a little bit further down the rabbit hole I then read about VS Code's Remote - Containers extension, which:
sounded exactly like what I was looking for - creating and using sandboxed development containers via Docker
looked relatively straight-forward and no risk to at least try out
uses the same foundations and shares a lot of the tooling and configuration behind GitHub Codespaces - so (in my opinion) is likely to become a more mainstream approach than tools such as Vagrant
I realised that the Remote Containers extension and GitHub Codespaces are also not new (though Codespaces is still in early access at time of writing), and I was a little late to the party, but I also figured that that might go in my favour as I've maybe skipped over some initial pain points that early adopters had.
Having not used WSL before the first step for me was to get that installed.
Installing WSL2
I found a few guides describing how to do this, but the one I followed was this one from the official WSL documentation. At the time (this may have changed depending on when you are reading this) I could not follow the "Simplified install" instructions because I was not on the Windows Insiders Program and didn't want to sign up to that right then. I followed the "Manual install" instructions instead.
The last step in the installation instructions is to install the Linux distribution of your choice. I am a Linux noob so picked Ubuntu because that is the default for the "Simplified install" and I thought I would just go with the default in the absence of any informed opinion of my own!
Now I had WSL2 and a Linux distro installed I could move on to the next step.
Windows Terminal is your friend
You will need to use a terminal / console app to interface with your WSL distro via a shell. I already was using Windows Terminal and the nice thing is that Windows Terminal has a feature called Dynamic profiles so it was already "aware" of my Ubuntu WSL distro when I next launched the app - I could immediately use it to open the default Ubuntu shell with no configuration required.
I did end up making one minor adjustment to the dynamic profile settings though, but that was because my default "starting directory" was set to a Windows path on a local disk. For my Ubuntu profile I wanted to set the starting directory to a path within my user's home directory, but there is a little bit of a trick to this as the startingDirectory
setting in Windows Terminal only accepts a Windows-style path. Thankfully good documentation came to the rescue again and it turned out that I just had to use the WSL UNC path to the home directory, which in my case looks like //wsl$/Ubuntu-20.04/home/meeg
.
Installing Docker Desktop
The development containers are Docker containers so I needed to install Docker Desktop for Windows. This went exactly as described in the documentation for me so I've got nothing more to say here!
VS Code's Remote - WSL extension
The Remote - Containers extension is just one of a few VS Code extensions in the "Remote" stable. There is also a Remote - WSL extension that I found I needed to install to act as a "stepping stone" to working with development containers. The reason being is that:
Windows container images are not currently supported by the Remote - Containers extension so I have to use Linux images
the development containers will bind mount the local file system into the container
the Docker Best Practices mention that to get the best performance when bind-mounting files into Linux containers you should store your files in the Linux file system rather than the Windows file system
(from what I found) you can't launch VS Code directly into the development container - you open your workspace locally and then you re-open VS Code attached to the container
because of the above Docker Best Practices "open your workspace locally" means opening the workspace in the Linux file system
"opening the workspace in the Linux file system" is easiest using the Remote - WSL extension
So I installed the extension, opened up my Ubuntu (WSL) shell in Windows Terminal, created a new directory to house my workspace, and ran code .
- I was now looking at VS Code running locally on Windows, but connected to the Ubuntu file system using the Remote - WSL extension.
I was now ready to try out remote containers.
VS Code's Remote - Container extension
First things first is that I need to install the Remote - Containers VS Code extension.
With that done I get a few new commands in my command pallette and the first one I want to run is Remote-Containers: Add Development Container Configuration Files
, which is the quickest way to setup a new development container.
When you run this command you will then be asked to select a container configuration definition, and you can select from options such as:
Language / platform specific such as C#, Go, Java, Node.js, Rust, Ruby etc, which are environments with the SDKs, frameworks, apps etc pre-installed and pre-configured for you to quickly start writing programs for those languages / platforms
"Empty" environments based on specific Linux distros such as Alpine, Debian, Ubuntu if you want a basically blank slate from which to configure your own environment
The full list of all available container configuration definitions, which includes all of the "official" configurations provided by Microsoft (including and in addition to those in the initial list), plus "community" contributions like Elm, Hugo, Julia and Swift
To try this out I picked the "C# (.NET)" container configuration and I was prompted with a few more options to choose my target .NET Version (I chose .NET 5 as I do not have that installed locally), and whether I also wanted to install Node.js and/or Azure CLI (I chose to install both just out of interest), and after making those choices my configuration was generated and placed in a .devcontainer
folder in the root of my workspace.
VS Code then detected that I had this .devcontainer
folder and prompted me to "Reopen in Container", which I did. This built and ran the container image and reloaded my VS Code instance attached to the running container - so now I was looking at VS Code running locally on Windows, but connected to the Docker dev container using the Remote - Containers extension.
I then wanted to get a basic .NET app created to test it out so I:
Started a new terminal session in VS Code and ran
dotnet new webapp -o WebApp
to create a basic Razor Pages web appOpened up
WebApp/Startup.cs
so that the C# VS Code extension kicked in and started installing Omnisharp and the .NET Core debugger etcOpened the command pallette and ran
.NET: Generate Assets for Build and Debug
to generate VS Code build and debug tasksThe C# extension may prompt you to create these anyway
Hit
F5
to build and launch the app in my default browserViewed the web app running on the dev container via
https://localhost:5001
You will get an untrusted certificate warning, which I just accepted at this point, but will look to fix
So there I can see that I have a basic .NET 5 development environment up and running through the Remote - Containers extension without having to install the required SDK locally.
Conclusion
Getting the basic tooling installed to support Remote Containers was fairly painless and where I did come across issues they were quickly solved from reading through the docs or a quick search online. It's a little bit awkward having WSL as a "stepping stone" on Windows, but I can't see that being much of a problem, if at all. I'm looking forward to trying out GitHub Codespaces where there should be even less tooling involved (maybe even no tooling if developing via the browser), and this is obviously not something you would encounter with Linux or macOS as your host - though as mentioned I haven't tried on anything except Windows so potentially there are peculiarities there also. Overall though the basic setup went well for me.
Once the initial setup is done the processing of selecting and spinning up a new dev container is super easy, and developing on it via VS Code is no different to developing locally. Granted I haven't yet done a lot of development with dev containers yet (I have done more than the example shown above though!) and I haven't worked on any large projects in a dev container so I am going to have to see how it goes, but I have not encountered any issues so far.
The only downsides I can see currently are that:
this is of course all going through VS Code so if you are used to using other tools instead of or alongside VS Code then this won't be for you unless you are prepared to compromise or look for workarounds - one example I have personally is that I like to use GitKraken, but switched to using the Git tools and terminal in VS Code when using the dev container
if disk space is at a premium then the WSL distro(s) and dev container image(s) are most definitely not going to help with that - I believe/hope there is a way to move the default installation paths for these though I have not had to look into it myself (yet)
The upsides are less obvious at this point because it's just me working on my project alone. Personally I'm happy I have gone through this process to learn a bit about WSL, Docker and Remote Containers generally and it's "cool" that I can spin up these different dev environments quickly now, but as one person working on one thing it's not a game changer.
However, I can see the potential here when working with a team of people (at work, in Open Source, or just for fun) that I now have a development environment defined alongside my code that other people can use to quickly spin up their own development environment that contains all of the required software and dependencies needed to work on that project.
Of course there are still some barriers to entry here - you need at least the basic tooling installed (and a machine that is capable of running the tooling!), plus you need to accept the downsides mentioned - but I can definitely see the benefits. And if the tooling required for Remote Containers is easier to setup than the full tooling required for the development environment then it actually lowers the barrier to entry and I can see this being appealing to Open Source work or to people working in development agencies where the tooling can change from project to project and cause versioning or configuration issues.
I'm going to keep using Remote Containers for a while in my personal projects and see how it plays out, and to make myself a bit more comfortable in this new environment I have done some work to configure and personalise my dev containers with my preferred Git config, VS Code extensions, and shell configuration (prompts, plugins, aliases) etc. I will cover the configuration and personalisation aspects of dev containers in my next article in this series.