Hey Nix friends!
I’ve been working on learning the basics of Nix for awhile now, in particular focusing on flakes. One of the things that still eludes me is the different use cases for the main flake commands.
When would you use nix build
vs nix shell
vs nix develop
vs nix run
? What are the strengths and weaknesses of each?
So far, I feel like nix develop is the command I am most familiar with from setting up my development environment on a web app, but I’d really like to understand all of them better.
- If you want to run a simple stand-alone binary just once, use nix run. E.g.
nix run nixpkgs#tree
(the command will have to live in$out/bin/
) (ormeta.mainProgram
, if that exists) - To pass it arguments, use
--
or your flags will be interpreted bynix run
, not passed to the command. E.g.nix run nixpkgs#tree -- -a
- To put that in the PATH of your current shell, temporarily, so you can run it a bunch of times today:
nix shell nixpkgs#ffmpeg
. Tip: to enable multiple programs:nix shell nixpkgs#{ffmpeg,tree,imagemagick}
. - Maybe your desired binary exists in
$out/bin
, but has a different name? This is an odd one:nix shell nixpkgs#postgresql --command psql ...
(extra odd: you don’t need to use--
to separate program args from nix args.) (I understand the “why” behind this, but I hate it. The newnix
command was supposed to solve the counterintuitive UI problem, but here we are, back to square 1. 🤷♀️) - Say you want to “build” a derivation, you use
nix build
, although if it’s available in a binary cache it will just fetch it from there:nix build nixpkgs#imagemagick
. Now you have it in your/nix/store
, and a symlink to that path in./result
. - Sometimes I just want to build something and print the path to stdout, without actually creating a link in
/nix/store
. Combine two flags:nix build nixpkgs#imagemagick --print-out-paths --no-link
.- I use that to do stuff like this:
$ nix run nixpkgs#tree -- $(nix build nixpkgs#imagemagick --print-out-paths --no-link)/bin
. - Or this, on mac:
open $(nix build nixpkgs#grandperspective --print-out-paths --no-link)/Applications/*
- You can see how
nix run
really is just syntactic sugar for$(nix build --no-link --print-out-paths )/bin/
(sort of)
- I use that to do stuff like this:
But my all time favourite is
nix develop
: it plops you in a build shell for a derivation. This means you can pretend you are the nix builder, locally. Including fetching the source!E.g., to build sbcl (the lisp compiler) locally, without installing anything else nor even downloading the source, just:
$ cd $(mktemp -d) $ nix develop nixpkgs#sbcl $ unpackPhase $ cd * $ patchPhase $ eval "$buildPhase" $ .... debug problems here...
I think this is very neat.
(question: I only know to use patchPhase as a function and buildPhase as a var to eval because I know the derivation well, but there has to be a deterministic way to invoke this, no?)
Summary:
- nix shell: alters your shell. Use it when you want to use a package.
- nix develop: for developing something. Only use it when you are developing a package.
- nix run: one-off running of programs.
- nix build: ensure a derivation is available in your /nix/store. Either download or build. Optionally: create symlink in ./result, and/or print the output path to stdout, and/or print build noise to stderr (
--print-build-logs
).
- If you want to run a simple stand-alone binary just once, use nix run. E.g.