Search code examples
nix

Does nix always create virtual environment deterministically


{
  inputs = {
    nixpkgs.url = github:nixos/nixpkgs/nixpkgs-unstable;
    flake-utils.url = github:numtide/flake-utils;
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {

        devShells.default = with pkgs; mkShell {
          packages = [ cargo rustup rustc rust-analyzer rustfmt cmake ];
          RUST_SRC_PATH = rustPlatform.rustLibSrc;
        };
      }
    );
}

This is a flake.nix necessary to set a virtual environment with rust thanks to the command

    nix develop -i

the i say only the elements defined in the flake are used. No program other than the program defined in the virtual environment can be used.

     rustup toolchain list
> 1.70  //OK

    rustup toolchain install 1.69
    rustup default 1.69
    rustc --version
 > rustc 1.69.0 (84c898d65 2023-04-16)   // OK

Now I quit the virtual environment

  exit

Then I reopen it.

    rustc --version
> 1.69

It means that the version of rust has been stored somewhere. It means that nix isn´t deterministic. The same flake can produce different environment. Is that a bug or are there others things that I should know.


Solution

  • Building Nix components (on systems where this is both supported and enabled) happens in a network-free, limited-filesystem-access sandbox where only declared dependencies are available. (It's still not a 100% guarantee of identical output every time because there's pollution from the outside world via things like the system clock, various random number generators, timing differences and load differences changing when process task switches take place, etc etc).

    nix develop does two things: First, it builds a devshell and its dependencies (where the build steps happen in the sandbox); then it starts an interactive shell process in the user's regular environment. This latter step, of interactively using a devshell, does not take place in this sandbox. No documentation I'm aware of claims otherwise.

    • When -i is passed, $HOME isn't exposed (usually folks using -i/--ignore-env also use --keep HOME to override this), but if a program figures it out (f/e, with an account lookup), it's perfectly capable of caching content under HOME/.cache/rustup or any similar location.

    • If you want each devshell invocation to have a new and different HOME directory, it's your job to do this yourself -- which you can do easily; mkdir "$TMPDIR"/home && HOME=$TMPDIR/home will do nicely, since starting nix develop creates a new and unique TMPDIR.