VS Code Remote Containers with Nix

Tom
Level Up Coding
Published in
3 min readAug 31, 2021

--

Photo by ammiel jr on Unsplash

Nix already provides a neat way to create a development environment with strong guarantees in terms of reproducibility. However, it still requires developers to install it on their computer which, even though Nix doesn’t install anything in directories such as /usr, can be met with some resistance.

On the other hand, VS Code’s devcontainers allow a developer to start working on a project without too much hassle granted they use VS Code and have Docker running. They also might feel better about things running in containers and not polluting their host system.

Here, I’ll describe how to bring these two solutions together and get the best of both worlds. One can use Nix directly on their host system or use VS Code and develop inside a container with the Nix environment.

This is the setup I have been using for a few months now on some projects. Initially, I was concerned that the overhead of a container might become a problem but it turned out that fear was unfounded.

Nix environment

This setup is using Nix Flakes to create a development environment. The entrypoint is in flake.nix at the root of the project.

The flake exports a devShell which is defined in nix/shell.nix as follows. This is just a dummy development environment with the Haskell compiler for the sake of demonstration.

nix/shell.nix

Running nix flake upgrade for the first time will create a new file flake.lock with pinned versions of Nixpkgs and flake-utils. This file should be version-controlled with the rest of the project.

These basic files are already useful for anyone willing to run Nix on the host rather than inside a container. The command nix develop drops you into a shell with this Nix development environment.

Docker image

We now need a Docker image with Nix and some more configuration to automatically load the development environment when started.

Since this relies on Nix Flakes, we need Nix 2.4 (prerelease as of this writing) instead of the latest “official” version.

The Dockerfile is in .devcontainer/ together with the configuration for Remote Containers since it’s specific to this usage.

.devcontainer/Dockerfile

This Dockerfile copies .devcontainer/nix.conf inside the container. It’s used to set some options that are needed to make Nix 2.4 work but it’s also a convenient way to share configuration amongst developers. For example, you could set substituters and trusted-public-keys to add some Nix cache.

.devcontainer/nix.conf

Finally, .devcontainer/profile.sh loads the Nix development environment as follows.

.devcontainer/profile.sh

Don’t forget to make this file executable by running the following command.

chmod +x .devcontainer/profile.sh

The file .devcontainer/.profile can be added to .gitignore or equivalent. It is only there to make sure the development environment is not garbage collected if you run nix store gc.

Configuration for the container

The final step is to tell VS Code how to load the devcontainer. This is achieved by adding a configuration at .devcontainer/devcontainer.json.

.devcontainer/devcontainer.json

A volume called yourproject_nix will be created and mounted at /nix. This implies that you won’t need to recompile all Nix derivations after rebuilding the container.

In this example, the VS Code’s extension bbenoist.nix is automatically installed in the container. You might want to add other extensions specific to your project’s tech stack.

If you like stories like this one, consider a membership to Medium and/or subscribe.

--

--