WARNING
None of this is authoritative! Please take everything with a grain of salt, and as this is a wiki post, please consider editing it. The whole point of this post is to initiate a discourse and to improve the official documentation; see the diagram in How to contribute to documentation.
Thank you!
-
- About Nix shells
- 0.1 What are Nix shells?
- 0.2 Use cases (and sources of confusion)
- 0.2.1 Develop Nix packages
- 0.2.2 Create ad hoc environments
- 0.2.3 Examples
- 0.2.3.1
nix-shell -p
- 0.2.3.2
nix-shell
with adefault.nix
- 0.2.3.1
- 0.3 Benefits of using Nix shells
- 0.4 How to start applications from the terminal that won’t close with the terminal?
-
- One-liners with pinning
- 1.1 Get a single package with
nix-shell -p
+callPackage
- 1.2 Improvise a
shell.nix
on the terminal- 1.2.1
mkShell
vsmkDerivation
- 1.2.2 Advantages of
mkShell
- 1.2.1
-
- Using
nix-shell
withshell.nix
- 2.1 What is
shell.nix
? - 2.2 Issues & solutions
- 2.2.1
nix-shell
messes with locales - 2.2.2 How to make
nix-shell
clean up after itself?
- 2.2.1
- Using
- 2.x Examples
TODO: Still a draft; have about 40 tabs open with
nix-shell
related stuff.
0. About Nix shells
0.1 What are Nix shells?
nix-shell
and nix develop
start an interactive Bash shell, and, depending on their arguments, they will
-
pre-populate the subshell with stuff (e.g., environment variables are set, applications will be available in
PATH
) -
start and configure services (e.g., launch a PostgreSQL instance and set it up)
-
?
QUESTION: Does
nix run
belong here? What else is missing?
TODO: Weave the Development environment with
nix-shell
nixos.wiki article into this - or vice versa.
0.2 Use cases (and sources of confusion)
0.2.1 Develop Nix packages
Instead of building (or realizing) the package (e.g., software, book, data set) with nix-build
(or siblings), one will get dropped into a Nix shell with all the dependencies and requirements (e.g., environment) of the package right before it would be built / realized.
That is, if the packages build instructions only ask for make
to be executed, then if you issue make
on this shell, then the package will be built, and a link will be provided to the result.
0.2.2 Create ad hoc environments
Examples:
-
developing an application, so you need a couple of scripting language runtimes with packages, debugging tools, a database instance running in the background, etc.
-
ops work on local/remote VMs (or one just has 12 old ThinkPads lying around) with different operating systems, so firing up a Nix shell with the tools one is used to (of specific versions!)
-
?
0.2.3 Examples
0.2.3.1 nix-shell -p
$ nix-shell -p peek treesheets yt-dlp
will drop you in a shell where the applications peek
, treesheets
, and yt-dlp
are readily available.
0.2.3.2 nix-shell
with a default.nix
TODO: There should be a better example (perhaps a small example repo), but couldn’t come up with anything better this quickly… (Or, at least, add (or redirect to places with) info about
-E
andcallPackage
.
Let’s take the treesheets
package for example:
$ git clone https://github.com/NixOS/nixpkgs.git
$ cd nixpkgs/pkgs/applications/office/treesheets/
$ nix-shell -E 'with import <nixpkgs> { }; callPackage ./default.nix { }'
$ <the build commands themselves, e.g., cmake>
If you try to execute treesheets
in this shell, you’ll get
treesheets: command not found
0.3 Benefits of using Nix shells
-
Make NixOS / Home Manager configuration lighter by only putting installables there that require extra config.
QUESTION: Although, these could be Nix shellified too, right?
-
Avoid depending on channels (and thus improving reproducibility) by evaluating the Nixpkgs package collection fetched at a specific commit (or the same with an alternate or one’s own package collection).
QUESTION: Flakes do something similar, don’t they?..
-
Create a sub-shell with tools for a specific project (e.g., PostgreSQL with config, web framework with all its dependencies)
-
To work on machines / VMs that are temporary, rarely used, reset periodically, etc., so just install Nix and use
nix-shell
with the most common tools with the required versions. -
?’
0.4 How to start applications from the terminal that won’t close with the terminal?
$ nohup google-chrome-stable & > ~/.nohup.out # or /dev/null
$ disown
See this great answer on nohup
vs disown
.
1. One-liners with pinning
NOTE: Using the
google-chrome
package to also demonstrate dealing with unfree packages. See 2.3. Installing unfree packages in the Nixpkgs manual.
ASIDE: tidbits on
builtins.fetch*
functions
builtins.fetchTarball
can handle ZIP archives too:fetchTarball "https://github.com/NixOS/nixpkgs/archive/<nixpkgs_commit>.zip"
builtins.fetchGit
could have been used in the examples below just as well:fetchTarball "https://github.com/NixOS/nixpkgs/tarball/<nixpkgs_commit>"
translates to
fetchGit { url = "https://github.com/NixOS/nixpkgs"; rev = "<nixpkgs_commit>"; }
Some extra reading regarding
builtins.fetchGit
:
+ (discourse) Difference betweenfetchgit
andfetchGit
+ (gist) an extended, off-the-rails version of thefetchGit
documentation
+ (stackoverflow) Git revisions vs references
+ (discourse)rev
andref
attributes inbuiltins.fetchGit
(and maybe flakes too?)
1.1 Get a single package with nix-shell -p
+ callPackage
TODO: Add aside about
callPackage
.
NIXPKGS_ALLOW_UNFREE=1 \
nix-shell -v -p \
'(callPackage \
(fetchTarball https://github.com/NixOS/nixpkgs/tarball/dc849ffbcd93c2a23e99dcc94efb0962594b8b5f \
) {} \
).google-chrome'
1.2 Improvise a shell.nix
on the terminal
NOTE: I believe
-E
or--expr
is only documented in 7.1. Common Options in the Nix manual (or, at least) the only place I saw it in the man pages was inman nix-instantiate
, but even there it is just mentioned.)
An ad hoc development sub-shell:
NIXPKGS_ALLOW_UNFREE=1 \
nix-shell -v -E \
"let \
pkgs = import (fetchTarball ${NIXPKGS_TARBALL}) {}; \
in \
pkgs.mkShell { \
buildInputs = with pkgs; [ google-chrome elixir postgresql ]; \
}"
1.2.1 mkShell
vs mkDerivation
Older (< 2018) scripts tend to use mkDerivation
instead of mkShell
, but the latter is only a convenience wrapper around the former when writing Nix expressions for nix-shell
. See mkShell needs more documentation in the manual #58624 Nixpkgs issue for more info and links.
1.2.2 Advantages of mkShell
(For creating dev environments, that is.)
-
accepts an
inputsFrom
list, to “to pull all the dependencies together” from a large project, for example. From the initial PR formkShell
:[…] you have multiple projects each with their own
shell.nix
, and you could create 1 mastershell.nix
that merges all subproject’sshell.nix
in case you wanted to work on all the subprojects together. (CMCDragonkai’s comment)Imagine you have a monorepo with multiple services, each in their own folder with their own default.nix that you can nix-shell and nix-build. On the top-level you can then add a shell.nix with,
mkShell { inputsFrom = [ serviceA serviceB ];
to pull all the dependencies together. (zimbatm’s comment) -
shellHook
s are also composed form input expressions. See PR #63701 for examples.
Tons of good info in this Elixir Discourse thread.
2. Using nix-shell
with shell.nix
2.1 What is shell.nix
?
TODO: Come up with a beginner friendly intro that can be embedded later into the official docs, because they are lacking at the moment:
- The Nix manual only mentions
shell.nix
twice, no examples.- The Nixpkgs manual’s Chapter 14. Special builders is probably the best (and only official) one, but it is very minimal. (Also, this was the first time I saw the
inputsFrom
attribute - every single example I’ve ever encountered usedbuildInputs
thus far.)- There are plenty of examples in the Nixos.wiki, but they are scattered all over the place, and no dedicated entry.
2.2 Issues & solutions
2.2.1 nix-shell
messes with locales
Had this issue with a more elaborate shell.nix
that sets up a Phoenix web project with a PostgreSQL instance (e.g., configures common options, creates tables, does the migrations), and the database kept failing with locale issues.
The solution is from this gist referring to Nixpkgs issue #318: nix-shell sets unsets LANG and friends, specifically to this comment:
The reason it doesn’t work is that you need to set $LOCALE_ARCHIVE, for instance by adding
LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive";
to the shell expression. (Maybe we should pass LOCALE_ARCHIVE even in --pure mode, but I’m not sure.)
2.2.2 How to make nix-shell
clean up after itself?
Use trap
. It’s a shell built-in; see more on it by typing help trap
.
- example 1: (unix & linux stackexchange) Killing background processes started in nix-shell
- example 2: this
2.x Examples
- (gist) Shell for Phoenix projects with node.js and PostgreSQL (by @ejpcmac)
- (gist) Example shell.nix for Nerves projects, setting both Erlang and Elixir versions (by @ejpcmac)
- (gist) Elixir/Phoenix Nix and script configuration
- (gist) A simple nix-shell script to establish an environment for Phoenix, Elixir and PostgreSQL development