VS Code Remote Containers with Nix
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.
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.
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.
Finally, .devcontainer/profile.sh
loads the Nix development environment as follows.
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
.
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.