I use Nix to manage dev/build dependencies. With it, I can declaratively ensure that dev/build dependencies for a project (node, rustup, rustfmt etc) are automatically installed and available on any computer that I work on. The tools are scoped to the project’s local nix-shell, so I can manage versions independently between projects. I only install nix once when setting up my machine, then when I cd into a project folder direnv runs nix to load the dependencies necessary for that project, but you can also just run nix-shell to start a shell with the dependencies loaded.

some things I like about it:

  • In a team setting it can ensure that no-one needs any specialised instructions to configure their machine for local dev. Instead they just cd into the project, and they can already build and run it
  • Packages can be pinned to a specific version
  • It can be extended to CI build machine setup
  • the config file is actually a script, where you can execute shell commands, and set up env vars as well
  • there is a project called home-manager which allows you to use nix for declaratively managing your OS user level packages/applications as well

a simple setup

  1. install nix
  2. install direnv and install the shell hook
  3. in your project folder, create a .envrc file and add the text use nix. You will need to run direnv allow after creating this file.
  4. still in your project folder, create a shell.nix file, and add the following contents:

     { pkgs ? import <nixpkgs> {} };
     pkgs.mkShell {
         buildInputs = []
  5. go to and search for any packages you need to run/build your project.

    For example, let’s add nodejs and pnpm.

    1. Searching for “nodejs” should yield the correct result in the first entry.
    2. Click on the name, select the tab titled “On non-NixOS” in the expanded block, and referencing the code block, copy the text nixpkgs.nodejs into the buildInputs list (note that you don’t need commas between items) and replace nixpkgs with pkgs
    3. Following the same process with pnpm, you will notice that the package is namespaced to nodePackages. This is fine, you should end up with pkgs.nodePackages.pnpm in your buildInputs.

    The file should now look like this:

     { pkgs ? import <nixpkgs> {} };
     pkgs.mkShell {
         buildInputs = [
  6. pressing the enter key or cding out and back into your project folder should now trigger an installation of the declared packages. When it’s done, which node should print a path in /nix/store (not sure if this will be the same under all OSes though).
  7. You’re done now. You can finally rest and watch the sun rise on a grateful universe.


  • the “OS” part of the name “NixOS” is a reference to a linux distro that integrates deeply with the nix tool, but you don’t have to use the OS to try/use the tool.
  • I found the website somewhat confusing when I first tried it, don’t be discouraged by this.
  • here’s a base snippet I use when starting a new nix file.
  • when using it with direnv, it takes a couple of seconds to load your shell when you cd into the project. To solve this I use nix-direnv which caches the shell and dramatically speeds it up