Node2Nix setup very slow


I’m learning Nix and trying to apply it to web development. At this stage I got a working setup with Node2Nix and Parcel. The whole code is here. The problem is that it’s very slow to enter the environment (about 50 s, and it’s a very bare bone app). Below I describe how I set things up hoping for an advice.

First I use node2nix to generate Nix expressions from NPM’s lock file, like this:

node2nix \
  --development \
  --lock package-lock.json \
  --composition node-dependencies.nix

Then I call nix build with the following expression (in default.nix file) to build the dist/ directory with final HTML, JS etc. and install it to the Nix store:

with import <nixpkgs> {};
  nodeDependencies = (
    pkgs.callPackage ./node-dependencies.nix {}
pkgs.stdenv.mkDerivation {
  name = "nix-for-web-development-playground";
  src = ./.;
  buildInputs = [ pkgs.nodejs pkgs.utillinux nodeDependencies ];
  buildPhase = "make dist";

  inherit nodeDependencies;

The dist target in the Makefile is basically this:

ln -s $(nodeDependencies)/lib/node_modules ./node_modules
parcel build src/index.html

This works in principle. I can call:

nix build
npx serve result

and have the app served on http://localhost:5000/. I can also call nix-shell default.nix to get a working development environment. This is great.

The main problem now is that booth commands take about 50s, even on subsequent executions without any changes to the code. I thought that after first build all it would take is to link the paths from Nix store and set some environment variables. But apparently there is a lot going on. In the build logs I see a lot of:

unpacking source archive /nix/store/[...].tgz

that seems to refer to Node dependencies. Then there is

pinpointing versions of dependencies...

That takes quite long. Then

patching script interpreter paths in .

with a lot of

interpreter directive changed from "/usr/bin/env node" to "/nix/store/jq3lr9b5kzhxn3yjrgxpab543y8nx9dj-nodejs-12.18.4/bin/node"

then a lot of

Adding metadata fields to: node_modules/[...]/package.json

Then running postinstall hooks on some modules.

Then some shrinking, patching and rewriting before the final build is performed. Here is a complete output from nix-shell default.nix (about 2300 lines).

I understand why most of these steps are needed, but why does it have to happen every time I enter the environment? Can I avoid it?

Thank you and please consider that I’m early in my learning process.

I experience same issue. It is slow and not clear where is bottleneck because CPU and network bandwidth are 0.

I got much better results by using Lorri. Check out this commit in my repo: Integrate Lorri and Direnv.

I have a suspicion that this can be also fixed with Flakes, but I didn’t figure out how to use them yet.

1 Like