Difference between nix-shell and "nix run"?


I’m trying to define common Nix shells as overlays, but somehow nix run refuses to use them.

For example:

self: super:

  go-dev-shell = mkShell {
    buildInputs = with self; [

    shellHook = ''
      export GO111MODULE=on
      export GOPATH=$HOME/.go

nix-shell -A go-dev-shell '<nixpkgs>' works fine, but nix run -f '<nixpkgs>' go-dev-shell always complains about This derivation is not meant to be built, aborting.

From what I’ve learned, mkShell is a simple wrapper around stdenv.mkDerivation that disables all build phases (see pkgs/build-support/mkshell/default.nix) - there shouldn’t be any special handling for mkShell in nix-shell, right?

Therefore I cannot understand what is the difference between nix-shell and nix run :frowning:.

1 Like

nix-shell gives you an environment where the dependencies of the supplied expression are available, not the package itself.
That’s why derivations created with mkShell cannot be built.

nix-shell has the -p flag that behaves as you expected (i.e. provides a shell with the given package). e.g.:

nix-shell -p gimp --run gimp

Yeah. I understand that mkShell derivation cannot be built. Problem is: how does nix-shell treat it differently while nix run doesn’t?

Or: I want to replace nix-shell with nix run. Is that possible?


nix-shell -A go-dev-shell '<nixpkgs>' will run the equivalent of nix run -f '<nixpkgs>' delve go gotools + some shell setup.

I see nix run for the first time, but from the manual page it would seem that no.


nix-shell doesn’t build the package itself. I haven’t looked into the details but AIUI it doesn’t run the phases for the derivation, though I think it makes them available as shell functions. mkShell disables building by defining a single phase that always aborts. nix run does build the derivation and makes it available to the shell. It’s like nix-shell -p, which I would assume would abort on a mkShell the same way nix run does.


tl;dr nix run is a “new interface”, part of nix 2.0. This new interface has no implementation for the feature from nix-shell that you are looking for.

nix run pkg1 pkg2 supersedes nix-shell -p pkg1 pkg2, but nix-shell -A pkg has no equivalent. The magic of nix-shell -A pkg is that it goes trough all the dependencies of pkg and starts a shell with these packages in the environment.

To reproduce that, you need to be able to extract all the dependencies of a package, which is done in nix-shell (around here and here) but has no equivalent in nix run (here) because it has no extraction logic.

If you want to emulate nix-shell with nix run, you may consider using nix run $(nix-build $(nix-store -q --references $(nix-instantiate) | grep ".drv$" | grep -v bash)) which still uses the old interface to obtain the .drv (nix-instantiate), to extract the dependencies (nix-store -q --references) and to build these .drvs (nix-build). The new nix build interface does not print the outputs, and therefore cannot be used here. the others have no equivalent.

nix 2.0 interface is still a work in progress, you should stick with nix-shell to get a shell from a derivation seen as an “environment” for that shell.


Thanks for that explanation!

Is there the intention to implement something like nix shell in nix 2.x or is the plan to keep nix-shell around?


That would be a question for @edolstra, but here is what seems likely to happen from my point of view, which may be very wrong.

nix-shell contains a lot of ad-hoc hacks to setup the environment, and this stuff does not really pertain to nix per se. So I guess there is little chance to see nix-shell ported as-is. More probably, the functionnality will see a large reactoring to move most of the custom hacks into nixpkgs and cleaner, well-defined interfaces.

This is a lot of work, and no-one seems too eager to start working on it as nix-shell is still fully supported, maintained, and working as intended.