Difference between nix-shell and "nix run"?

More than 10 months passed since the last reply here - are there any news regarding nix-shell in the new interface? Maybe there is some ticket on the topic?

3 Likes

Going to quote @lilyball’s reply verbatim from another thread because it is highly relevant:

BTW the “better option” here is nix run . nix-shell -p foo sets up the environment as it would for building foo , so all of foo 's dependencies are in your PATH and all of the other environment vars are set too (I count 23 env vars containing NIX ), but nix run produces an environment much more similar to what you’d get if you simply installed the package (e.g. it basically just prefixes your PATH with the appropriate directories from the specified package).

That said, nix-shell replaces your normal shell initialization files and nix run doesn’t, so depending on how your .bashrc is set up you might override the PATH . For example with my setup, echo $PATH in a nix run shell starts the path with /usr/local/bin before the nix-provided path value, because my current .bashrc unconditionally prefixes my PATH (which I really should fix).

2 Likes

If I have a shell.nix file, I can simply run

❯ nix-shell
these paths will be fetched (0.05 MiB download, 0.28 MiB unpacked):
  /nix/store/qgbwdnk91rk26b5bkd6qv5r6c2v733kb-bash-interactive-4.4-p23-dev
copying path '/nix/store/qgbwdnk91rk26b5bkd6qv5r6c2v733kb-bash-interactive-4.4-p23-dev' from 'https://cache.nixos.org'...

[nix-shell:~/dev/research/julia/odecontrol]$

Can I do something similar with nix run? I haven’t been able to make it work:

❯ nix run
error: opening file '/home/skainswo/dev/research/julia/odecontrol/default.nix': No such file or directory

nix 2.3 or 2.4pre?

For 2.3, no, run does not read from a shell.nix, as the content of a shell.nix is not meant to be built. run though builds and then gives the build result in the environment. nix-shell and nix shell (2.3) though do not build the mentioned derivations, but instead their buildinputs and give those into the environment.

2.4s run works completely different from both, and require a flake with applications.

3 Likes

nix develop is more similar to that usage, you can also use nix shell, but nix shell is more similar to nix-shell -p

1 Like

What is the difference between nix shell and nix develop? From nix --help for 2.4pre:

nix develop - run a bash shell that provides the build environment of a derivation

nix shell - run a shell in which the specified packages are available

but I still can’t mentally differentiate between the two, unlike nix run, which clearly has a different purpose now - to run nix apps.

2 Likes

nix develop takes a single derivation, and gives you a shell to make it (all dependencies needed to build it will be exposed in the shell).

nix shell, essentially adds packages to the current shell. (Technically forks the shell, and in the child shell will add the packages)

Example:

nix develop nixpkgs#ripgrep → You will have rust, pkg-config, and a few other packages added to your shell, but not ripgrep

nix shell nixpkgs#ripgrep → You will have access to ripgrep, and nothing else will be added.

5 Likes

not really the current shell.

I think a better definition would be:
nix shell will start a new shell where all the packages you give it are available to use.

So nix develop is analogous to nix-build while nix shell is analogous to nix-shell.

But then there is also nix build.

Maybe it’s futile to draw similarities between the old and the new commands, just take them for what they are.

@jayesh.bhoot I think a better breakdown would look like this:

use old command new command
start a new shell that has ripgrep available nix-shell -p ripgrep nix shell nixpkgs#ripgrep
start a new shell that has the dependencies required for building ripgrep available (like gcc and rustc) nix-shell -A ripgrep nix develop nixpkgs#ripgrep
build ripgrep completely within Nix. Put the resulting binary in a result/ symlink nix-build -A ripgrep nix build nixpkgs#ripgrep
run an application defined in a flake nothing similar? nix run nixpkgs#ripgrep

A couple things to keep in mind:

  • nix build, nix develop, and nix run have special support for flakes. For instance, if there is a flake.nix in the current directory, then:

    • nix build with no arguments will try to build the outputs.defaultPackage.
    • nix develop with no arguments will load the shell defined in outputs.devShell.
    • nix run with no arugments will run the command defined in outputs.defaultApp.
  • You might be able to make the argument that nix run is similar to nix-shell -p ripgrep --run 'ripgrep', but this is somewhat of a stretch.

  • nix shell may be renamed: Rename 'nix shell' · Issue #4715 · NixOS/nix · GitHub

11 Likes

Thank you. This, combined with Flakes - NixOS Wiki, give us a lot of clarity.

The most authoritative page on the nix command seems to be Nix command - NixOS Wiki. You should consider putting your response there.

1 Like

While the wiki does have a lot of good info, I’d recommend you make sure to read through all the man pages for nix. By now I think these pages have a lot more info on the new-style Nix commands:

https://nixos.org/manual/nix/unstable/command-ref/experimental-commands.html

1 Like

Previously it was nix-shell -p pkg --run "some command"

nix run is for running applications defined in flakes. But flakes allow you to run arbitrary applications, not just binaries from a given derivation.

I think nix-shell -p pkg --run "some command" is closer to nix shell nixpkgs#pkg --comand "some command".

Fair, I don’t think there was a way to just run a given binary in the old cli. Or it would be hideous like $(NIX_PATH=remote.domain.org/toplevel.tar.gz nix build -A pkg)/bin/<command> ..., but at that point, you’re just scripting instead of using a cli.

1 Like

This is the best response in the thread imho, however I am still left more confused than I was before from this discussion (I saw it in an email digest and was curious).

I think the problem is that a lot of the responses in the thread presume what you’re trying to do and provide responses in that context. Eg that it provides the “build environment” for the current package - what if the current package is mkShell and I’m just trying to set up an environment?

Do I use nix-shell, nix run, nix develop, or nix shell? I think it’s nix shell, but some of the responses in this thread make me think maybe I want nix develop. Right now I’m using nix-shell.

And without this comment I have no context for the man page comment that “ run a shell in which the specified packages are available”, specifically where the “specified packages” come from. (packages, buildInputs, nativeBuildInputs, etc).

I didn’t dig into the man page documentation deeply and probably never will, unless my life revolves around building things with nix. But if it’s just one tool that I’m using, I’m going to learn what I need to in order to accomplish the tasks that I have. I simply don’t have the time to read dozens of pages about functionality that I may never use.

Ah, I can see how this would be confusing.

If you have a derivation defined with mkShell, the “normal” way to jump into it is nix develop.

nix develop is generally want you want for a development shell (or what you’re calling a “build environment”).

As you’ve seen, you can sometimes also use nix shell for this purpose. Although there are a couple corner cases where nix develop will do what you want, and nix shell probably won’t. nix shell won’t set some environment variables in the resulting shell the same way that nix develop does. nix shell also won’t do things like run shellHook.

(Of course, this information isn’t in the man pages, so I don’t blame you for being confused here. It doesn’t help that mkShell has Shell in the name, which suggests that nix shell should be used.)

That’s completely understandable, and I imagine most users feel the same way.

Nix is a pretty small community, and the documentation really isn’t that good. I imagine you probably don’t want to spend the time to write issues, but for anyone else reading this, it is always helpful for new users to create issues in GitHub - NixOS/nix: Nix, the purely functional package manager or GitHub - NixOS/nixpkgs: Nix Packages collection & NixOS about documentation that is lacking. Even better would be PRs!

Also, the new-style Nix commands are currently experimental, so I’d suggest not using them if you are new to Nix. Once you get comfortable with Nix, figuring out the new-style commands should be relatively easy.

2 Likes

I think you can’t use nix shell with a mkShell derivation because nix shell will try to build the derivation (as opposed to nix develop building the dependencies) and since mkShell derivations are not buildable, it will fail.

Ah, good point.

I think I got a little confused when responding to @spease.


To sum up, if you are working with a derivation defined with mkShell, you generally want the new-style nix develop command, or the old-style nix-shell command.

Trying to build a derivation defined with mkShell with a new-style command like nix run, nix shell, or nix build (or an old-style command like nix-build or nix-shell -p) should result in an error.

If you are working with a derivation defined with something like buildenv or haskellPackages.shellFor (which are both similar to mkShell but don’t error out when building), then in theory the new-style commands nix develop, nix shell, nix build, etc will all work, but nix develop is probably what you want.

Thanks for being understanding. I wouldn’t say I’m new to nix, I first started using it as a package manager for macOS ~2019, and then got more deeply involved to try and use it for CI in 2020. In 2021, I’ve submitted a few PRs to nixpkgs, including fixing the fundamental iOS build stuff, and I’m using it on my desktop m1, along with a custom derivation for macOS apps/packages that I meant to upstream awhile back. I’ve got ~600 lines of nix locally to install various macOS GUI apps that aren’t in nixpkgs.

However, I do feel like I still don’t know that much and am still an outsider? The big issue being that there’s so much complexity and special cases, as well as multiple sources of documentation. I think I wound up pulling a lot from both the nixos and nixpkgs documentation, as well as having to spend a considerable amount of time reading nixpkgs itself. Which isn’t great. Error messages have often been unhelpful as well (though I think at least some of the specific complaints I had were pending and may have been merged right now).

Eg an update broke my darwin-configuration.nix awhile back, and all I know is that the override I had to add my installApplication to nixpkgs.darwin does not seem to be working (I get ‘installApplication missing’ in my other packages). And I haven’t fixed it because I perceive it as requiring a ‘deep dive’ (I took a stab at it today and so far it looks like it’s an accurate assessment after 20-30 minutes). But I digress.

I think I was a little too ambiguous for what I’m doing. I have a backend I’m working on where I need to set a few shell variables - further down the line I figured I might take a stab at setting the environment packages and using --pure so I would have a consistent environment regardless of updating my system packages. So I’m not building software so much as executing various commands or launching docker/docker-compose instances. Basically I’m trying to use it as a power-virtualenv that can handle any kind of packages, not just python.

So basically right now my default.nix consists of a few variables being set and a shellHook to run a single mkdir -p command. Typically I enter the environment with just nix-shell.

While what I’ve been doing is working, this discussion has raised some doubt in my mind that I’ve been doing things correctly.

Although as I check, both my Arch Linux and macOS machines even have nix develop (2.3.15 vs 2.4pre I guess). And I guess I need a much longer command to keep nix itself up-to-date. Though to be fair, maybe this is handled by darwin-rebuild. Or maybe I need to do nix upgrade-nix? sigh. At one point I knew this.

Well, right now I’m using mkShell. I’ve heard of buildEnv somewhere, but trying to find the tradeoffs via Google search doesn’t yield terribly helpful results.

Even in the first one, someone is already having to delve into the source. And it doesn’t really answer the question of what each one is for, and what guarantees it makes. The problem with delving into the source to understand stuff is that it’s impossible to know what’s ‘guaranteed’ behavior as part of the function contract, and what’s current implementation-specific stuff that’s presumed internal. So it makes it hard to build on stuff without it breaking.

Anyway, sorry for my long comment and derailing the thread. Wasn’t my original intention to go this in-depth, I just figure that most people who run into these issues end up simply going silent, so the people that are left are the people that tend towards being willing to tolerate the status quo. When nix works, it’s fantastic, and actually easier to use than some other package managers. A basic derivation is relatively clear about what it’s pulling in. It’s mostly just the learning curve, like where simple questions like this wind up leading to a complex discussion.

I’m not sure if it’s just better documentation or if there needs to be a stronger curation of what’s functionally ‘external’ for most users (simpler) vs ‘internal’ for people working on nix itself (more complex).