What are Dev Containers, What are GitHub Codespaces, and Why Should You Care? A Practical Intro06 Nov 2021
GitHub Codespaces made waves when GitHub introduced the feature several months ago. Codespaces promises a lot. It solves configuration headaches, it creates disposable environments, it can provide better performance than the laptop you’re working on, and it can better enable remote collaboration. I was interested to dip my toes in the water and see what it’s like to work with Codespaces, but that can be a little tricky to do if you don’t have access to a GitHub org that’s paying for Codespaces. Fortunately, as it turns out, there’s a very accessible alternative that’s free and easy to try, and it’s a great way to get a taste of what the Codespaces experience might be like.
VS Code has a feature called Dev
Containers, and it works
almost exactly like GitHub Codespaces, but it runs locally in Docker rather than in
the cloud. Of course, this means you don’t get the performance benefits that are
possible with Codespaces, but most of the other benefits still apply. In
particular, a dev container provides a disposable development environment with
all the right tools ready to go. In fact, GitHub Codespaces use the
devcontainer.json file a local dev container would use - a Codespace is really
just a dev container in the cloud.
What is a Dev Container?
To really understand dev containers (or Codespaces) and the benefits they provide, I think it’s important to understand the problem they’re trying to solve. Just for fun, I maintain a small open source project called Script Seed. It isn’t anything terribly exciting – it’s just a collection of sample “Hello World” scripts in various scripting languages. But because that project uses 12+ different scripting languages, it really shows the usefulness of a dev container. I don’t have all 12+ languages from that project installed on my laptop, so I use Docker to test it.
Even before dev containers were popular, I included a Dockerfile and a Makefile in Script Seed. The Dockerfile was an Ubuntu container that had all the supported scripting languages installed, so you could test any script in the project. And the Makefile provided a user-friendly syntax for running the container. This Dockerfile was, essentially, a dev container. It provided anyone who wanted to develop Script Seed a pre-configured, disposable workspace inside the container with all the tools needed to develop and run the project. A true VS Code dev container takes that same principle to the next level by adding some conventions, improving the experience, and integrating tightly with the IDE.
Setting up a VS Code Dev Container (or Codespace)
Recently, I took that same project (Script Seed) and migrated my old homegrown Dockerfile to a true VS Code dev container. In setting up this small example project with a dev container, I learned some things along the way that I’ll be able to apply when setting up larger projects with dev containers or Codespaces. I think a small project like this that’s easy to understand provides a perfect playground to learn about dev containers. Here’s the PR where I added the dev container to Script Seed. Let’s see what I did.
In the first
I add a
devcontainer.json to the to the
folder and I add a
bin/server script. The
bin/server script is just a helper
copied from my old docker entrypoint, so let’s focus on the other two files. The
best way to begin setting up a dev container is to let VS Code generate the
files for you by selecting Remote-Containers: Add Development Container
Configuration Files… from the command palette, as documented in Create a
there’s already a Dockerfile in your project, you’ll be prompted to start from
that Dockerfile or from a predefined configuration definition. Although I first
tried to use my existing Dockerfile, I went back and did the process again using
a predefined container, and had a better first-time experience with the
predefined container. The VS Code team has done a really nice job setting up
these predefined containers, so I’d recommend trying one of them so you can
understand all the benefits they include before trying to build your own
container. In particular, these dev containers provide a non-root user, provide
a nice Bash prompt, and install many tools you might want.
Starting from the predefined Ubuntu
I installed all the languages I needed (as I had in my other Dockerfile) and was
ready to go! I made a couple light customizations to
including naming my container and forwarding a port for the HTTP server to run
In the second
in my PR, I update my old Makefile to use the new dev container. Primarily, I
want to use
make test to run tests in CI (with GitHub Actions), but this is
also a nice pattern to allow anyone who isn’t using VS Code to interact with the
dev container. Finally, in the third
I update my documentation to describe the new dev container.
Once the dev container is set up, using it is easy. Just open the project in VS
Code with the Remote
extension installed, and it will prompt you to re-open the project inside the
container. (You can try this yourself by cloning Script
Seed if you want.) The first run will
be a little slow as it pulls and builds the Docker container. Subsequent starts
will be faster as long as the Docker image doesn’t need to be rebuilt, and there
are some advanced options I haven’t played with yet to improve startup time by
pre-building containers. Inside the IDE, you’ll be able to interact with tools
installed in the container, including opening a terminal in the IDE that will
get you to a shell in the container. You can specify extensions to install in
devcontainer.json file and those extensions will be automatically
installed when you open the container in the IDE. You can do things like run and
debug code inside the container using tools from the IDE. If your project
includes a web server, you can run it inside the container (in my case, the
bin/server script) and open the site in a browser on the host computer via a
One thing you might be wondering about is customization. If you’re working primarily inside the container, how do you interact with your usual tools like git, ssh, or vim, and how do you customize those tools if the config for the container is shared? Fortunately, VS Code has already thought about this and has some good solutions for dev containers. For example, there’s a mechanism to share Git credentials with the container either with the git credentials helper or with SSH agent forwarding. VS Code Dev Containers also provides a way to customize the container with your own dotfiles. I haven’t played with this too much yet, but I’ve started modifying my own dotfiles to work better with this kind of setup. Perhaps I’ll write more about that experience later.
In summary, dev containers provide a ton of value and are relatively easy to set up – particularly if you’re already familiar with Docker. Normally, a lot of the friction you experience when you open a new project for the first time is getting your development environment set up. You might need several languages, compilers, and tools installed on your system, and you might need new extensions installed in your IDE. That could take hours to set up, and using a dev container removes a lot of that friction and provides a great way to manage a consistent, disposable development environment that’s more inviting for new users. And GitHub Codespaces take it to the next level by hosting that environment in the cloud.