# Explore Nix development environments

One of Nix's key features for developing software is Nix [development environments][dev].
You can define development environments of any complexity using the [Nix language][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`][nix-develop] command activates a Nix environment:

```shell title="Enter a Nix development environment defined in an external flake"
nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example"
```

<Admonition warning title="This could take a while" id="nix-develop-loading">
  The first time you activate a Nix development environment using `nix develop`
  it's likely to be a slow operation. That's because Nix needs to build every
  [package][packages] included in the environment from scratch. This is in
  contrast to most [package managers][pkg], which install things more quickly
  because they download pre-built archives like tarballs. Future `nix develop`
  invocations should be much faster, as Nix doesn't need to build the packages
  again.
</Admonition>

You should be greeted by a new shell prompt, something like this:

```shell title="Shell prompt in the Nix development environment"
(nix:zero-to-nix-env) bash-5.2$
```

:rocket: **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:

```shell title="Ensure that curl and Git are installed"
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):

<NixStorePath pkg="curl-8.1.1-bin" bin="curl" />

What happened here? The [Nix] CLI did a few things:

- It used the `https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#example` [flake reference][flakes] to pull in some Nix code and built a specific [flake output][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`][path] that enables the `git` and `curl` packages to be discovered in the [Nix store][store].

Two other things that you can provide in Nix development environments:

1. 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][hooks], are properly set up.
     Run this to see an example shell hook:

     ```shell title="Run a shell hook"
     nix develop "github:DeterminateSystems/zero-to-nix#hook"
     ```

1. 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.
   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]) to `development`, `dev`, and so on.

Let's leave the Nix development environment to prepare for the next section:

```shell title="Leave the environment"
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!).

## Run commands inside the development environment

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:

```shell title="Layer multiple environments"
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][store] and nothing from your global environment.
As you can see, Nix development environments are [_hermetic_][hermeticity] in that they're isolated from the surrounding environment (such as your environment variables and paths like `/bin` and `/usr/bin`).

## Language-specific environments

As we did in the [last section][last], let's get a bit more specific and explore how Nix can benefit more specific programming environments.
Select one of these programming languages:

<Languages />

Now explore the Nix development environment for <Language />:

<SpecificLanguage lang="C++">
```shell title="Explore a development environment for C++"
nix develop "github:DeterminateSystems/zero-to-nix#cpp"
```

First, let's see the Nix store path for [CMake]:

```shell title="Get path information for CMake"
type cmake
```

Check the current CMake version:

```shell title="Check CMake's version"
cmake --version
```

</SpecificLanguage>
<SpecificLanguage lang="Haskell">
```shell title="Explore a development environment for Haskell"
nix develop "github:DeterminateSystems/zero-to-nix#haskell"
```

First, let's see the Nix store path for the [Glasgow Haskell Compiler][ghc] (GHC):

```shell title="See the store path for GHC"
type ghc
```

Check the current GHC version:

```shell title="Check the GHC's version"
ghc --version
```

</SpecificLanguage>
<SpecificLanguage lang="JavaScript">
```shell title="Explore a development environment for JavaScript"
nix develop "github:DeterminateSystems/zero-to-nix#javascript"
```

First, let's see the Nix store path for [Node.js]:

```shell title="See the store path for Node.js"
type node
```

Now use Node to run a program:

```shell title="Check the Node.js version"
node --eval "console.log('1 + 1 = ' + (1 + 1))"
```

</SpecificLanguage>
<SpecificLanguage lang="Python">
```shell title="Explore a development environment for Python"
nix develop "github:DeterminateSystems/zero-to-nix#python"
```

First, let's see the Nix store path for Python:

```shell title="See the store path for Python"
type python
```

Now use Python to run a program:

```shell title="Use Python to run a simple program"
python -c "print(1 + 1)"
```

</SpecificLanguage>
<SpecificLanguage lang="Go">
```shell title="Explore a development environment for Go"
nix develop "github:DeterminateSystems/zero-to-nix#go"
```

First, let's see the Nix store path for the [Go] CLI:

```shell title="See the store path for Go"
type go
```

Now check the Go version:

```shell title="Check the Go version"
go version
```

</SpecificLanguage>
<SpecificLanguage lang="Rust">
```shell title="Explore a development environment for Rust"
nix develop "github:DeterminateSystems/zero-to-nix#rust"
```

First, let's see the Nix store path for [cargo]:

```shell title="See the store path for Cargo"
type cargo
```

Now create a [Rust] project in the current directory and run the example:

```shell title="Initialize a Rust project and run it"
cargo init ./zero-to-nix-rs
cd ./zero-to-nix-rs
cargo run
```

You should see `Hello, world!`.

</SpecificLanguage>
<SpecificLanguage lang="Scala">
```shell title="Explore a development environment for Scala"
nix develop "github:DeterminateSystems/zero-to-nix#scala"
```

First, let's see the Nix store path for [sbt]:

```shell title="See the store path for sbt"
type sbt
```

Check the sbt version inside the environment:

```shell title="Check the sbt version"
sbt --version
```

</SpecificLanguage>

Like usual, run `exit` to leave the Nix environment and return to your usual environment.

## Beyond language-specific environments

In the [previous section](#language-specific-environments), 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:

```shell title="Activate a multi-language development environment"
nix develop "https://flakehub.com/f/DeterminateSystems/zero-to-nix/*#multi"
```

This Nix environment has several tools available:

- [Python]
- [kubectl]
- [OpenTofu]
- [OpenSSL]

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][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.

<Admonition
  success
  open
  title="Nix development environments and direnv"
  id="nix-develop-direnv"
>
  [direnv] is a popular tool that automatically loads specific environment
  variables whenever you `cd` into a directory (and then unloads those variables
  when you `cd` out of the directory). The combination of direnv and Nix can be
  quite powerful, enabling you to automatically load Nix development
  environments whenever you navigate to a directory. For more info, see
  [Effortless dev environments with Nix and direnv][nix-direnv] on the
  [Determinate Systems blog][blog].
</Admonition>

## From a local flake \{#flake}

Earlier in this guide, we activated a Nix development environment defined in a [flake][flakes] 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:

<Languages />

To get started in your <Language /> project, create an empty directory and initialize a [flake template][templates]:

<SpecificLanguage lang="C++">
```shell title="Initialize a flake template for C++"
mkdir nix-cpp-dev && cd nix-cpp-dev
nix flake init --template "github:DeterminateSystems/zero-to-nix#cpp-dev"
```
</SpecificLanguage>
<SpecificLanguage lang="Haskell">
```shell title="Initialize a flake template for Haskell"
mkdir nix-haskell-dev && cd nix-haskell-dev
nix flake init --template "github:DeterminateSystems/zero-to-nix#haskell-dev"
```
</SpecificLanguage>
<SpecificLanguage lang="JavaScript">
```shell title="Initialize a flake template for JavaScript"
mkdir nix-javascript-dev && cd nix-javascript-dev
nix flake init --template "github:DeterminateSystems/zero-to-nix#javascript-dev"
```
</SpecificLanguage>
<SpecificLanguage lang="Python">
```shell title="Initialize a flake template for Python"
mkdir nix-python-dev && cd nix-python-dev
nix flake init --template "github:DeterminateSystems/zero-to-nix#python-dev"
```
</SpecificLanguage>
<SpecificLanguage lang="Go">
```shell title="Initialize a flake template for Go"
mkdir nix-go-dev && cd nix-go-dev
nix flake init --template "github:DeterminateSystems/zero-to-nix#go-dev"
```
</SpecificLanguage>
<SpecificLanguage lang="Rust">
```shell title="Initialize a flake template for Rust"
mkdir nix-rust-dev && cd nix-rust-dev
nix flake init --template "github:DeterminateSystems/zero-to-nix#rust-dev"
```
</SpecificLanguage>
<SpecificLanguage lang="Scala">
```shell title="Initialize a flake template for Scala"
mkdir nix-scala-dev && cd nix-scala-dev
nix flake init --template "github:DeterminateSystems/zero-to-nix#scala-dev"
```
</SpecificLanguage>

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][flakes] for your project.
- The `flake.lock` [pins][pinning] all of the [flake inputs][inputs]&mdash;essentially the Nix dependencies&mdash;in your `flake.nix` file to specific [Git revisions][rev].

One of the [flake outputs][output] of this Nix [flake][flakes] is a [development environment][dev] for <Language />.
To enter that development environment:

```shell title="Enter a development environment in the current flake"
nix develop
```

Now that we've entered the development environment, we can do some exploring, starting with [Nix store paths][paths].

<SpecificLanguage lang="C++">
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:

```shell title="Get information about the gcc executable"
type gcc
```

You should see a (rather strange) path like this:

```shell title="Path information for gcc"
gcc is /nix/store/nbrvvx1gyq3as3ghmjz62wlgd8f3zfpf-gcc-wrapper-11.3.0/bin/gcc
```

</SpecificLanguage>
<SpecificLanguage lang="Haskell">
Ordinarily when you run `type ghc` on a Unix system, you get a path like `/usr/bin/ghc`.
Try running it in the Nix development environment:

```shell title="Get path information for ghc"
type ghc
```

You should see a (rather strange) path like this:

```shell title="Path information for ghc"
ghc is /nix/store/f3qnvw5gxgxxpr275kf97pfcy2n1gv79-ghc-9.2.4/bin/ghc
```

</SpecificLanguage>
<SpecificLanguage lang="JavaScript">
Ordinarily when you run `type node` on a Unix system, you get a path like `/usr/bin/node`.
Try running it in the Nix development environment:

```shell title="Get path information for Node.js"
type node
```

You should see a (rather strange) path like this:

```shell title="Path information for Node.js"
node is /nix/store/i88kh2fd03f5fsd3a948s19gliggd2wd-nodejs-18.12.1/bin/node
```

</SpecificLanguage>
<SpecificLanguage lang="Python">
Ordinarily when you run `type python` on a Unix system, you get a path like `/usr/bin/python`.
Try running it in the Nix development environment:

```shell title="Get path information for Python"
type python
```

You should see a (rather strange) path like this:

```shell title="Path information for Python"
python is /nix/store/a9mmam4km4bjnkzl62533w7d0wyrhrj9-python3-3.13.6/bin/python
```

</SpecificLanguage>
<SpecificLanguage lang="Go">
Ordinarily when you run `type go` on a Unix system, you get a path like `/usr/bin/go`.
Try running it in the Nix development environment:

```shell title="Get path information for Go"
type go
```

You should see a (rather strange) path like this:

```shell title="Path information for Go"
go is /nix/store/5bcx8rv6sy33xsf5dzkp9q8lfdqrsiwa-go-1.19.4/bin/go
```

</SpecificLanguage>
<SpecificLanguage lang="Rust">
Ordinarily when you run `type cargo` on a Unix system, you get a path like `/usr/bin/cargo`.
Try running it in the Nix development environment:

```shell title="Get path information for cargo"
type cargo
```

You should see a (rather strange) path like this:

```shell title="Path information for cargo"
cargo is /nix/store/zc1nr87147gvmg5nqci8q5cfnzg82vwp-rust-default-1.64.0/bin/cargo
```

</SpecificLanguage>
<SpecificLanguage lang="Scala">
Ordinarily when you run `type sbt` on a Unix system, you get a path like `/usr/bin/sbt`.
Try running it in the Nix development environment:

```shell title="Get path information for sbt"
type sbt
```

You should see a (rather strange) path like this:

```shell title="Path information for sbt"
sbt is /nix/store/p0hca7x8g45p5hnh0xjzy5s2bcpy1i9l-sbt-1.7.3/bin/sbt
```

</SpecificLanguage>

Probably not what you expected! What happened here? A few things:

- Nix looked at the `devShells` [flake outputs][output] in `flake.nix` to figure out which [Nix packages][packages] to include in the development environment (Nix specifically looked at the `packages` array).
- Nix built the packages specified under `packages` and stored them in the
  [Nix store][store] under `/nix/store`.

[bash]: https://gnu.org/software/bash
[blog]: https://determinate.systems/posts
[cargo]: https://doc.rust-lang.org/stable/cargo
[cmake]: https://cmake.org
[curl]: https://curl.se
[dev]: /concepts/dev-env
[direnv]: https://direnv.net
[flakehub]: https://flakehub.com
[flakes]: /concepts/flakes
[ghc]: https://haskell.org/ghc
[git]: https://git-scm.com
[go]: https://go.dev
[hermeticity]: /concepts/hermeticity
[hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
[inputs]: /concepts/flakes#inputs
[kubectl]: https://kubernetes.io/docs/reference/kubectl
[language]: /concepts/nix-language
[last]: /start/nix-run
[nix]: /concepts/nix
[nix-develop]: https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-develop
[nix-direnv]: https://determinate.systems/posts/nix-direnv
[node.js]: https://nodejs.org
[openssl]: https://openssl.org
[opentofu]: https://opentofu.org
[output]: /concepts/flakes#outputs
[packages]: /concepts/packages
[path]: https://en.wikipedia.org/wiki/PATH_(variable)
[paths]: /concepts/nix-store#store-paths
[pinning]: /concepts/pinning
[pkg]: /concepts/package-management
[python]: https://python.org
[rev]: https://git-scm.com/docs/revisions
[rust]: https://rust-lang.org
[sbt]: https://scala-sbt.org
[store]: /concepts/nix-store
[templates]: /concepts/flakes#templates