I’m a new user of Nix, discovering its possibilities as part of an initiative to fix our wildly varying dev envs in my company. I’m planning to write some (initially internal) introductory docs to Nix, so I need to understand everything quite thoroughly myself first.
Right now, after some looking around, I find myself now quite confused with regards to what I expected to be mere syntactical differences about
nix shell and
I am quite confused about these. Can some explain in layman’s terms what’s the difference between these 3 commands and which should be used when?
I can’t help but provide some feedback, I’ll make it as benevolent as possible: I am 100% confident that every user I will introduce Nix to will be confused about these command being available, similar yet different. Naming is hard, we all know that, but identifying ~bad~ suboptimal naming is actually very easy, and it seems, to me at least, that this is a typical case here.
Unfortunately, I can’t provide any suggestions as I’m not aware of the differences between the three commands. Otherwise, I would have been happy to suggest something.
Thanks a lot for the fantastic project, community, and your help!
I’m not sure which descriptions you’re reading, but after glancing at the summary at the top of each manpage I guess part of the problem (especially for new users) may be when the documentation describes what the tool does, while leaving it up to the reader to understand why, or what we use it for?
Part of the confusion is because there are two versions of CLI here.
nix shell and
nix develop (and do some degree
nix run) inherit some of the things people use
nix-shell for. There’s some intrinsic potential for confusion in that relationship.
Edit: I'm hiding my descriptions below. tejing's answer conflicts and is presumably correct. I won't delete them, but I don't want to exacerbate confusion.
The synopsis for
nix shell says:
run a shell in which the specified packages are available
So, the missing motivational bit is that you’d generally use this for tasks like:
- building a development environment where you want specific software to be available for whatever you’re developing per
- opening a shell with a handful of tools available that wouldn’t normally be on your path (maybe to try out a handful of similar tools that you haven’t used before)
The synopsis for
nix develop says:
run a bash shell that provides the build environment of a derivation
The motivational bit here is that it’s for working on a derivation. This is basically the work nix-shell was initially created for IIUC.
There is, indeed, a long discussion about this Rename 'nix shell' · Issue #4715 · NixOS/nix · GitHub (and, of course, this was much discussed before then).
Thanks a lot for the reply, the GH thread you shared is very enlightening with regards to the various positions.
The one-liner for
nix shell is quite explicit and I like it.
To be more specific,
nix-shell is easily confused with
nix shell in its naming, and
nix develop is confusing in and of itself
Do you think it’d make sense to instruct my readers to focus on the set of
nix COMMAND and skip the
nix-XXX commands initially, until they’re more resourceful about Nix?
I think it’d be helpful to know the story here:
nix-shell was originally designed to debug derivation build processes. You hand it a derivation, and instead of building it, it drops you into a shell very similar to the derivation’s build env, so you can walk through the stdenv phases and explore what’s actually happening.
nix-shell turned out to have 2 other significant and unforseen usecases. Over time, features were added to support them:
- Making development environments. Rather than debugging a nix build, people would just work on their source code from within the shell.
pkgs.mkShell was added to support this usecase.
- Temporarily getting access to a package. People would use
nix-shell when they wanted to run something without actually permanently installing it.
nix-shell -p made this much much easier.
When the flakes-centered CLI redesign came along, one of the things people wanted to do was split this up a bit:
nix shell is built for that last usecase (temporary access to a package). All it does is add a package’s
bin directory to
PATH, without any of the development stuff. It also takes multiple package arguments, as that makes sense for this usecase. It’s similar to
nix-shell -p foo when not used for development purposes, as it doesn’t do all the stdenv stuff that makes dev tools work properly, but
nix-shell -p actually does.
nix develop takes on the other 2 usecases (debugging derivation builds and development environments). Its argument is still the package whose build environment one wants to enter, and you can still use
pkgs.mkShell to create a “package” with a build environment good for development work. It’s similar to
nix-shell -A foo,
nix-shell foo.nix, and
So there’s some replication of functionality because we’re in the middle of a CLI redesign which is still experimental (
nix shell and
nix develop are still experimental, so
nix-shell is sticking around despite doing the same thing). There are also some unforseen usecases which we’re now trying to support in less hacky ways.
In some ways, this feels similar to complexity of git, and the meaning of push, pull, fetch, checkout etc, that confuse many initially. Over time pretty good diagrams and graphical illustrations have evolved to illustrate the relationships. I have not seen something along these lines for nix , and perhaps it is not easily done, but sometimes diagrams can bring home a point that is much harder to do with words.
Hmm. The outline by @tejing conflicts with how I interpreted/represented what we’d use for our own development environments (for some purpose other than working on a package/derivation), and I’m inclined to assume tejing has it right since I’ve largely been avoiding flakes and the new CLI myself.
I’ll let that speak for itself about how we’re doing, here
The current situation is very far from ideal. The community is fairly fragmented right now between flakes and the ‘new’
nix CLI versus the old CLI, and to a lesser extent between channels and more declarative approaches like
niv. This means that documentation can be confusing or even contradictory.
I was going to complain that good up-to-date newbie-friendly docs aren’t easy to find, but I checked the Learn Nix page on the NixOS site and it is looking great, actually! Go check it out! It suffers from the issue above in that it starts of with
nix-shell, but that’s probably better for new users to avoid putting them in a flakes silo.
I do want to complain a bit about nix-shell, or really
pkgs.mkShell. I had a weird problem recently that turned out to be because
mkShell propagates the python build dependency of a mkPythonApp, despite the entire purpose of the
mkPythonApp being to avoid exposing the python binary to the PATH. I solved my problem by using
buildEnv instead, but somebody with less experience or motivation would probably have just quit. The reason for the issue is that mkShell is meant to help debug the Nix derivation itself, which is something I essentially never do. Nix doesn’t really have anything that fits perfectly for building an environment for developing an application apart from the default.nix derivation, and I think the faster new users are pointed toward something like devenv the better.
I very much appreciate the honesty here, thanks for that. One thing that has not been highlighted in this conversation so far is: Is it well known where the project is heading? I mean, if I wanted to walk the (a?) safe, future-friendly path, what would it look like? Even if that meant that path was a bit bumpy at times, I’d prefer that over a soon-to-be deprecated hard-earned knowledge of old APIs. As an example, is it worth it to invest time in flakes? Are they here to stay?
I’d also welcome a “we don’t know” answer, of course.
Flakes are here to stay, but the may/will change from what we currently have. They are an experimental feature and not yet stabilzed.
They have their flaws, which in some peoples need to be tackled rather sooner than later.
system-system in flakes is far from ideal, crosscompiling is a hard topic and even “unfree” software is a problem that has yet to be solved.
Another issue reporting of error locations, though that will be improved a lot when the “lazy tree” feature lands, which is actively worked on.
Perhaps the answer to this has been given starting at 15:30 in this recorded video from the foundation board?