How to include binaries in PATH for images built with Nix?

Hi, how can I introduce new binary to the $PATH in container built with Nix?

I have the following code, and I have to run a shell script to fix playwright in Nix/Scratch environment. It is using which, and I tried to put pkgs.which into the image, but the command is not found right now:

{ pkgs, package, fix-playwright-browsers, ... }:
pkgs.dockerTools.buildLayeredImage {
  name = "portfolio_pdf";
  tag = "latest";
  contents = with pkgs; [ nodejs-16_x google-chrome-dev firefox-bin which ];
  extraCommands = ''
    which node
    # ${fix-playwright-browsers.outPath}/fix-playwright-chromium
  '';
  config = { Cmd = [ "node" "${package}/dist/index.js" ]; };
}

How can I fix this? This is the link to the shell-script I am referring to:

1 Like

So I discovered a docker image for playwright, and I decided to give it a try.

{ pkgs, package, fix-playwright-browsers, ... }:
let
  playwright = pkgs.dockerTools.pullImage {
    imageName = "mcr.microsoft.com/playwright";
    imageDigest = "sha256:776f868d63ff001fd8d17a7cfd4ff4fd5dfd6f3d07aec457232d0ced53e64ef6";
    sha256 = "sha256-BdLuTD/YggLMF62dZ2K93okOXACaWH7xuZlPYW4QRwU=";
  };
in pkgs.dockerTools.buildLayeredImage {
  name = "portfolio-pdf";
  tag = "latest";
  fromImage = playwright;
  contents = with pkgs; [ nodejs-16_x ];
  # extraCommands = ''
    # cp -r "${package}" "./app"
  # '';
  config = { Cmd = [ "echo" "hello"]; };
  # config = { Cmd = [ "node" "./app/dist/index.js" ]; };
}

But even I am just trying to echo, I get the following error. Really clueless right now, and I think the error is coming from the baseImage

nix-shell ❯ docker run -p 3001:3001 portfolio-pdf:latest
standard_init_linux.go:228: exec user process caused: no such file or directory

I’m guessing that the extraCommands environment is quite limited - which seems to match the documentation at pkgs.dockerTools | nixpkgs

Could you try writing a bash script which does your npm install, then calls the fix playwright script, and then call your node command as your layers config command?

That does defer a bit of work to startup time, but it should have a full path to work with.

echo is a shell built-in, not a binary, so that won’t work. There is no “echo” binary to run, you’d need to launch a shell and call the echo command.

~~Have another look at section 14.2.2.1 here (bit down, linking paragraphs is hard on mobile): https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-dockerTools.~~

I.e., your binaries will be in /bin. You can just create a PATH variable with config.Env like so:

pkgs.dockerTools.buildLayeredImage {
  config = {
    Env = [ "PATH=/bin" ]; # Yeah, ugly, but that's the spec
  };
}

I’m not sure if that’s enough to make the binaries callable from Cmd without specifying their full path either, since that doesn’t go through a shell and therefore probably shouldn’t resolve binaries through PATH, but docker likes adding magic. If your playwright script respects PATH that should do the trick, though.

To call packages in Cmd like your node there you probably want to use the ${pkgs.nodejs}/bin/node shortcut as the binary as described a bit further down in the manual. That will automatically include the binary in the image and resolve the command correctly.

I also suspect your working directory may be muddled a bit, how about just using absolute paths like this:

pkgs.dockerTools.buildLayeredImage {
  name = "portfolio-pdf";
  tag = "latest";
  fromImage = playwright;
  extraCommands = ''
    cp -r "${package}" "/app"
  '';
  config = {
    Cmd = [ "${pkgs.nodejs-16_x}/bin/node" "./dist/index.js" ];
    WorkingDir = "/app";
  };
}

I’d also suggest making a proper derivation from your package and adding that to contents instead of copying with extraCommands, that way you don’t need to spin up qemu just to copy a directory :slight_smile:

Finally, to unconfuse your path situation, in case my guesswork is wrong, note that docker exec -it /bin/ash exists, as well as docker run -it /bin/ash, assuming you have the ash shell in the image - that would allow you to inspect the directory tree and debug that way. You can add ash to the contents temporarily by adding busybox to your contents.

Alternatively, you can export and inspect the tar directly using docker export.

Ignore me, @c00w is right, I somehow missed that you’re calling that through extraCommands. echo is still a shell builtin though.