title | path | summary | |||||
---|---|---|---|---|---|---|---|
Explore Nix development environments |
/start/nix-develop |
|
One of Nix's key features for developing software is Nix development environments. You can define development environments of any complexity using the Nix language. We'll cover that a bit later, but for now let's get a feel for what a Nix development environment is and how it works.
The nix develop
command activates a Nix environment:
nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example"
You should be greeted by a new shell prompt, something like this:
(nix:zero-to-nix-env) bash-5.2$
🚀 Success! You're now in a Bash environment that includes curl and Git. You may already have both in your environment, but run these commands to see that something new is happening:
type curl
type git
For curl, for example, you should see a strange path like this (the hash part should be different on your machine):
What happened here? The Nix CLI did a few things:
- It used the
https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example
flake reference to pull in some Nix code and built a specific flake output (more on this later). - It built the packages specified in the environment configuration (again, more on this later).
- It set up an environment with a
PATH
that enables thegit
andcurl
packages to be discovered in the Nix store.
Two other things that you can provide in Nix development environments:
- Although this example doesn't include one, you can define shell hooks, which are arbitrary shell code that runs whenever the environment starts up.
Some example use cases for shell hooks:
-
echo
information about the environment to the console whenever the environment is activated -
Run things like checks and linters
-
Ensure that other desired hooks, like Git hooks, are properly set up. Run this to see an example shell hook:
nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#hook"
-
- Nix development environments support environment variables as well.
Run
echo $FUNNY_JOKE
to access a (hilarious) value that's available only in the Nix environment (then runexit
to leave the environment). Some example use cases for environment variables:- Set logging levels using
LOG_LEVEL
or whatever is appropriate for the tools you're using. - Set the environment using variables like
NODE_ENV
(for Node.js) todevelopment
,dev
, and so on.
- Set logging levels using
Let's leave the Nix development environment to prepare for the next section:
exit
If you have Git installed, check your PATH
for it using type git
.
It should be at a global path like /usr/bin/git
.
And if you run echo $FUNNY_JOKE
again you should get an empty string (unless you happen to have that variable set on your machine!).
While it's fun to explore the environment, you don't always want to be inside the environment to use it.
The nix develop
command provides a --command
(or -c
) flag that you can use to run commands that use the environment but from your current environment.
Here are some examples for the environment we used earlier:
nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example" --command git help
nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example" --command curl https://example.com
In both cases, you're running a package in the Nix store and nothing from your global environment.
As you can see, Nix development environments are hermetic in that they're isolated from the surrounding environment (such as your environment variables and paths like /bin
and /usr/bin
).
As we did in the last section, let's get a bit more specific and explore how Nix can benefit more specific programming environments. Select one of these programming languages:
Now explore the Nix development environment for :
First, let's see the Nix store path for [CMake]:type cmake
Check the current CMake version:
cmake --version
type ghc
Check the current GHC version:
ghc --version
type node
Now use Node to run a program:
node --eval "console.log('1 + 1 = ' + (1 + 1))"
type python
Now use Python to run a program:
python -c "print(1 + 1)"
type go
Now check the Go version:
go version
You should get 1.22.5. First, let's see the Nix store path for cargo:
type cargo
Now create a Rust project in the current directory and run the example:
cargo init ./zero-to-nix-rs
cd ./zero-to-nix-rs
cargo run
You should see Hello, world!
.
First, let's see the Nix store path for sbt:
type sbt
Check the sbt version inside the environment:
sbt --version
Like usual, run exit
to leave the Nix environment and return to your usual environment.
In the previous section, we explored Nix environments tailored to specific programming languages. But Nix environments are infinitely flexible, enabling you to combine whichever packages you like. Let's explore an example of this:
nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#multi"
This Nix environment has several tools available:
As in the previous examples, you can run commands like type python
and type kubectl
to see that these tools are all discoverable in the Nix store and not somewhere like /usr/bin
.
This list could easily include 100 packages.
It's up to you.
We won't cover how to create these environments just yet, but we hope that you come away from this guide with a basic sense of what Nix development environments provide.
Earlier in this guide, we activated a Nix development environment defined in a flake on FlakeHub. While using an environment in this way is helpful, it's more common to use a development environment defined in a local flake in the current directory.
First, tell us which language you prefer:
To get started in your project, create an empty directory and initialize a flake template:
Once the template has been initialized, run ls .
to see the contents of the directory, which should include two important files:
- The
flake.nix
file defines the flake for your project. - The
flake.lock
pins all of the flake inputs—essentially the Nix dependencies—in yourflake.nix
file to specific Git revisions.
One of the flake outputs of this Nix flake is a development environment for . To enter that development environment:
nix develop
Now that we've entered the development environment, we can do some exploring, starting with Nix store paths.
Ordinarily when you run `type gcc` on a Unix system, you get a path like `/usr/bin/gcc`. Try running it in the Nix development environment:type gcc
You should see a (rather strange) path like this:
gcc is /nix/store/nbrvvx1gyq3as3ghmjz62wlgd8f3zfpf-gcc-wrapper-11.3.0/bin/gcc
type ghc
You should see a (rather strange) path like this:
ghc is /nix/store/f3qnvw5gxgxxpr275kf97pfcy2n1gv79-ghc-9.2.4/bin/ghc
type node
You should see a (rather strange) path like this:
node is /nix/store/i88kh2fd03f5fsd3a948s19gliggd2wd-nodejs-18.12.1/bin/node
type python
You should see a (rather strange) path like this:
python is /nix/store/cqbfx55481irqgbl3bw8jwf69vjpbp8r-python3-3.9.15/bin/python
type go
You should see a (rather strange) path like this:
go is /nix/store/5bcx8rv6sy33xsf5dzkp9q8lfdqrsiwa-go-1.19.4/bin/go
type cargo
You should see a (rather strange) path like this:
cargo is /nix/store/zc1nr87147gvmg5nqci8q5cfnzg82vwp-rust-default-1.64.0/bin/cargo
type sbt
You should see a (rather strange) path like this:
sbt is /nix/store/p0hca7x8g45p5hnh0xjzy5s2bcpy1i9l-sbt-1.7.3/bin/sbt
Probably not what you expected! What happened here? A few things:
- Nix looked at the
devShells
flake outputs inflake.nix
to figure out which Nix packages to include in the development environment (Nix specifically looked at thepackages
array). - Nix built the packages specified under
packages
and stored them in the Nix store under/nix/store
.