Nix Shell Container#

We focus here on the shells produced by the mkShell function.

The shell environments produced are not isolated from the environment of your machine.

Consider the following flake.nix:

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/23.05";
  outputs = { self, nixpkgs }:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs { inherit system; };
    in {
      devShells.${system}.default = pkgs.mkShell {
        packages = [ pkgs.gcc ];
      };
    };
}

When entering the shell (nix develop), the environment will contain the disired gcc on top of your current environment.

One way to isolate the shell would be to create a Docker container, build it, load it, and run it.

But this is cumbersome.

A better way would be to isolate the shell without any extra dependency.

The Nix4Science flake provides a way to do so by using the Linux user namespace feature.

First, let’s import the flake as n4s. This flake provides a replacement for the mkShell fonction: n4s.lib.${system}.mkShell. By default it behaves like mkShell, but we can activate the containerization of the shell by setting the containerize parameter:

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/23.05";
  inputs.n4s.url = "github:nix4science/n4s";
  outputs = { self, nixpkgs, n4s }:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs { inherit system; };
    in {
      devShells.${system}.default = n4s.lib.${system}.mkShell {
        containerize = true;
        packages = [ pkgs.gcc ];
      };
    };
}

Now, calling nix develop will start the shell in a containerized environment!

However, this method cannot accept to run command inside the container like nix develop --command gcc --version. This is because of the way Nix manages the --command flag.

Our workaround is the following:

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/23.05";
  inputs.n4s.url = "github:nix4science/n4s";
  outputs = { self, nixpkgs, n4s }:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs { inherit system; };
    in {
      packages.${system}.default = n4s.lib.${system}.mkShellContainer {
        packages = [ pkgs.gcc ];
      };
    };
}

Now, instead of running nix develop you have to run nix run. To pass a command, simply append the command after --:

nix run . -- gcc --version