Nix Workshop 

Part 1: Nix, Flakes & Nix DevShell

Gabriel Nützi, gabriel.nuetzi@sdsc.ethz.ch

Cyril Matthey-Doret, cyril.matthey-doret@epfl.ch

May 8, 2025 (Updated: Sep 3, 2025), Repository, Slides

Help

How to use these slides:

  • S: See the speaker notes and solutions.
  • Esc: See all slides and jump around.
  • Space: Go forward.
  • Shift + Space: Go backward.

Acknowledgment

Thanks to the following contributors:

Requirements

All examples refer to the workshop repository in the root directory.

Ensure that you have the requirements fulfilled.

Motivation

 Why Nix?

You start python and you get this:

>>> import numpy
Segmentation fault (core dumped)

💣 It Works on My Machine

🧪 Development Setups


Feature Local Development Mamba Devcontainer Nix/DevShell
Maintainenance ⚠️ High ⚠️ Medium ⚠️ Medium ⚠️ Medium
Reproducibility ❌ Low ⚠️ Medium ⚠️ Medium Very high
Ease of Use ❌ Low ✅ Easy ✅ Ok ❌ Low
Dep. Mgmt. ⚠️
Portability 💣 ⚠️
CI Stability 💣 ⚠️ ⚠️ Almost Perfect

Full Table

 What is Nix?

Nix is a software package management & deployment infrastructure:

  • 🏃🏻‍♂️Software is built and run in a predictable and reproducible way.

  • ✅ Builds are reproducible — same inputs give same results.

  • 🔄 Installs packages without breaking others.

  • 📦 Can create isolated dev environments.

  • 💻 Works on Linux, macOS.

Nix started 2006 …

Learn Nix the Fun Way

Why I love Nix, and you should too! 💖

Tool Dependencies

I am building a script to find my IP:

#! /usr/bin/env bash
curl -s http://ipinfo.io | jq --raw-output .ip
12.24.14.88

What guarantees that jq or curl is available?

Package It with Nix (1)

{
  system ? builtins.currentSystem,
  pkgs ?
    import
      (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/9684b53175fc6c09581e94cc85f05ab77464c7e3.tar.gz")
      {
        inherit system;
      },
}:
pkgs.writeShellScriptBin "what-is-my-ip"
''
  ${pkgs.curl}/bin/curl -s http://ipinfo.io | \
    ${pkgs.jq}/bin/jq --raw-output .ip
''

Package It with Nix (2)

Building this Nix code gives you a store path:

/nix/store/7x9hf9g95d4wjjvq853x25jhakki63bz-what-is-my-ip

which contains the script and all needed dependencies

#!/nix/store/mc4485g4apaqzjx59dsmqscls1zc3p2w-bash-5.2p37/bin/bash

/nix/store/zl7h70n70g5m57iw5pa8gqkxz6y0zfcf-curl-8.12.1-bin/bin/curl \
  -s "http://ipinfo.io" | \
  /nix/store/y50rkdixqzgdgnps2vrc8g0f0kyvpb9w-jq-1.7.1-bin/bin/jq \
    --raw-output ".ip"

Nix has encoded the used executables with store paths (/nix/store).

What Is This Hash ?

/nix/store/7x9hf9g95d4wjjvq853x25jhakki63bz-what-is-my-ip

The hash 7x9hf9g95d4wjjvq853x25jhakki63bz of your built package depends on:

  • source code of the package (the bash script)
  • dependencies down to the commit level (curl, jq)
  • all build instructions (nix code, including dependencies)

⛓️ Deterministic software packaging.

 The Nix Language

In a Nutshell

 The Nix Language

  • Domain-specific functional language (no side-effects).

  • Structurally similar to JSON but with functions.

  • Fundamental data types such as string, integer, path, list, and attribute set.

  • Lazy Evaluated: expression evaluation delayed until needed.

  • ⚠️Specifically designed for deterministic/reproducible software deployment.

 The Nix Language

  • Nix files *.nix - contain mostly one function.

  • The function below takes one argument banana and returns an attribute set { x = ... }:

    # myfunction.nix
    banana: # Function with one argument `banana`.
    let     # Define variables.
      number = 1;  # A number.
      list = [ 1 2 3 "help"];  # A list with 4 elements.
      set = { a = 1; b.c.d = [1]; };  # A nested attribute set.
      result = banana.getColor { v = number; };  # Calls another function `banana.getColor`.
    in
    { x = number; y = set.b.c.d; z = result; } # Return an attribute set.
  • For later: Watch this short introduction.

Examples

let # start for "procedural" statements
 mult = a: b: a * b;
 mult10 = mult 10; # Bind the first arg.
in
mult10 (mult 8 2)
# -> 160
let
f = args: {
  a = args.banana + "-nice";
  b = args.orange + "-sour";
};
in
f { banana = "1"; orange = "2" }
# -> { a = "1-nice"; b = "2-sour"; }
let
f = { ban, ora, ... }: { # Destructuring
  a = ban + "-nice";
  b = ora + "-sour";
};
in
f { ban = "1"; ora = "2"; berry ="3"; }
# -> { a = "1-nice"; b = "2-sour"; }
let
f = { list ? [] }: {
  a = builtins.map (x: x*x) list;
};
in f [ 1 3 9 ]
# -> { a = [ 1 9 81 ] }

More Examples

# Concat lists.
[ 1 2 3 ] ++ [ 1 2 3 ]
# [ 1 2 3 1 2 3 ];
# Merge attribute sets.
{ a = 1; b = 2; } // { a = 2; c = 3; }
# -> { a = 2; b = 2; c = 3; }
rec {
  b = 2;
  c = b + d;
  d = 10;
} # Discouraged: prefer `inherit`.
# -> { b = 2; c = 12; d = 10; }
# Lazy evaluation.
let
  x = abort "fail";
in
if true then 42 else x
# -> 42
# Import files.
let
  myfunc = import ./myfunction.nix;
in myfunc 1 + (import ./other.nix 3)

Attribute Set Building: inherit

# Inherit 'key = value'.
let
  width = 100;
  color = "blue";
  set = { b = 1; };
in
{
  inherit color;   # color = color;
  inherit (set) b; # b     = set.b;
}

Variable Interpolation

let
  key = "c"
  color = "blue";
  set = {
    c = { v = "hello-${color}" ;}
  };
in set.${key}.v

# -> "hello-blue"

Strings and Paths

let
  dir = ./.github/workflows;     # A path. Nix makes them absolute!
  file = "${dir}/gh-pages.yaml"; # Interpolated path gets added into the `/nix/store`.
in file
# -> "/nix/store/w9il9gvki2nfdzfc1lrlbiv3xy3mx90a-workflows/gh-pages.yaml"

Caution With let Statements

Do not reassign in let blocks:

let
  a = "hello";
  a = a + "world";
  #   ^
  #   |
  #  🆘 Endless recursion, this is not reassigning.
in a

✅ Configure nixd (Nix Language Server) in your IDE to see “Go to definitions”.

Questions ?

Try out the examples shown before yourself with nix repl (see slide)

Verify in the interactive Nix shell:

nix repl

Or pass standard input to nix eval:

echo 'let a = 3; in a' | nix eval --file -

Time: 3min

Revisit whats-is-my-ip

Building Our First Package (1)

Put the following in a script whats-is-my-ip.nix:

let
  system = builtins.currentSystem; # e.g. `x86_64-linux`

  # Download something into the `/nix/store/...-source`
  src = builtins.fetchTarball
    "https://github.com/NixOS/nixpkgs/archive/9684b53175fc6c09581e94cc85f05ab77464c7e3.tar.gz";

  # Import the `default.nix` in the `/nix/store/...-source`
  f = import src;

  pkgs = f { inherit system; }; # This is the package attribute set of `nixpkgs`.
in
pkgs.writeShellScriptBin "what-is-my-ip" ''
  ${pkgs.curl}/bin/curl -s http://ipinfo.io | \
    ${pkgs.jq}/bin/jq --raw-output .ip
''

Wait! What is github.com/NixOS/nixpkgs ?

󰳏 nixpgkgs is a mono-repository with Nix code to build software packages (> 130k).

 A commit in nixpkgs represents the version of all packages at that commit (a big tree).

What is github.com/NixOS/nixpkgs ?

Building Our First Package (2)

nix build -f ./examples/what-is-my-ip.nix --print-out-paths

> "/nix/store/7x9hf9g95d4wjjvq853x25jhakki63bz-what-is-my-ip"

Explore whats in /nix/store/7x9hf9g95d...-what-is-my-ip/bin/what-is-my-ip:

#!/nix/store/mc4485g4apaqzjx59dsmqscls1zc3p2w-bash-5.2p37/bin/bash
/nix/store/zl7h70n70g5m57iw5pa8gqkxz6y0zfcf-curl-8.12.1-bin/bin/curl \
  -s "http://ipinfo.io" | \
  /nix/store/y50rkdixqzgdgnps2vrc8g0f0kyvpb9w-jq-1.7.1-bin/bin/jq \
    --raw-output ".ip"

Building Our First Package (3)

pkgs.writeShellScriptBin "what-is-my-ip" ''
  ${pkgs.curl}/bin/curl -s http://ipinfo.io | \
    ${pkgs.jq}/bin/jq --raw-output .ip
''

Inspect the Dependency Graph

Run

nix-store --query --include-outputs --graph \
  $(nix build -f ./examples/what-is-my-ip.nix --print-out-paths) > graph.dot

nix develop --command dot -Grankdir=TB -Gconcentrate=true -Tpng graph.dot > graph.png

and inspect graph.png.

Inspect the Dependency Graph

What Is a Flake?

You have seen files like flake.nix lying around in repositories already.

A flake.nix [1, 2]

  • provides a deterministic way to manage dependencies and configurations [1].

  • comes with a flake.lock file which locks dependencies.

Remember fetchTarball ".../NixOS/nixpkgs/..." in what-is-my-ip.nix.
🌻 A flake.nix is a better method for locked inputs.

What Is a Flake? (2)

A flake flake.nix:

  • References external Nix code - called inputs.

    • Other repositories, local files, or URLs with a flake.nix.
  • Defines structured outputs (a function).

    • Specifies what the flake provides.
# flake.nix
{
  inputs = { /* ... */ };

  outputs = inputs: {
    packages = /* implementation */

    # ... other output attributes ...
  }
}

What Is A Flake? (3)

  • Nix evaluates a flake.nix by calling the outputs with inputs.

    • Try nix repl and :lf . to load the ./flake.nix in directory ..

    • Check outputs.packages.x86_64-linux = { ... }.

      Flat attribute set of Nix derivations.

      Note: Certain output attributes are system scoped, e.g. packages.x86_64-linux.

What Is a Nix Derivation?

A derivation is a

  • specialized attribute set, describes how to build a Nix package.

    { type = "derivation"; ... }

    Check nix repl -f <nixpkgs> and type pkgs.curl.type.

Whats a Derivation?

A derivation is a build instruction to realize a package in the /nix/store using a special derivation function.

  • Can depend on multiple other derivations.
  • Produce one or more outputs.

The complete set of dependencies required to build a derivation—including its transitive dependencies—is called a closure. [Ref]

Evaluate & Build a Derivation

  • Evaluating: Store build instructions in the /nix/store
    (a store derivation *.drv, more details).

  • Building: Realizing outputs of the derivation in the /nix/store. This can literally be anything!

Build A Derivation

Build the example derivation - eval. & realize it in the Nix store:

cd nix-workshop
nix build -L "./examples/flake-simple#packages.x86_64-linux.mytool" \
  --print-out-paths --out-link ./mytool

or in steps see appendix.

  • ℹ️ The string ./examples/flake-simple#packages.x86_64-linux.mytool is called an installable (see appendix).

  • 🩳 Short Form: nix build "./examples/flake-simple#mytool" uses builtins.currentSystem (works also for macOS users).

Build A Derivation (2)

✅ Inspect tree ./mytool:

/nix/store/blm702jzc...vd9gxp4c9n-mytool
└── bin
    └── mytool

✅ Run it with:

./mytool/bin/mytool -h
nix run "./examples/flake-simple#mytool"

Build/Run A Derivation - Github

You can also specify github repositories and nested flakes and build/run derivations on them:

nix build -L "github:sdsc-ordes/nix-workshop?dir=examples/flake-simple#mytool"
nix run "github:sdsc-ordes/nix-workshop?dir=examples/flake-simple#mytool"

Nix Development Shells

What Is a Nix DevShell?

Its a Nix derivation in the output attribute set devShells of the flake.nix:

{
  inputs = { /* ... */ };
  outputs = inputs: {
    packages.x86_64-linux = {
      mytool = /* derivation */
    };
    devShells.x86_64-linux = {
      banana-shell = /* derivation */
    };
    # ... other outputs ...
  }
}

The banana-shell derivation is meant to be consumed by nix develop.

Create A DevShell

The flake in ./examples/flake-simple defines devShells an output:

devShells.x86_64-linux =
  let pkgs = inputs.nixpkgs-unstable.legacyPackages.${system}; in
  {
    default = pkgs.mkShell {
      packages = [ pkgs.skopeo pkgs.cowsay ];

      shellHook = ''
        echo "Hello from Shell"
        ${pkgs.cowsay}/bin/cowsay
      '';
    };
  }
);

Function pkgs.mkShell makes a derivation consumable by nix develop:

nix develop "github:sdsc-ordes/nix-workshop?dir=examples/flake-simple#default" --command zsh

Use devenv.sh for Nix DevShells

  • 🚧 Nix DevShells from nixpkgs (pkgs.mkShell) are raw and too simplistic.

  • 🌻Nix DevShells from devenv.sh provides more concise configuration.

    • ❤️‍🔥Configuration based on mechanics which drive NixOS (NixOS Modules).

Questions ?

Workshop 🏑

Time to get 🫵r fingers dirty with the following exercises:

  1. Setup a flake.nix for a Go project.
  2. Write a function forAllSystems.
  3. Setup a Nix shell with https://devenv.sh.
  4. Add packages to the Nix shell.
  5. Add Nix shell capabilities.
  6. Add custom options to devenv for a run-it script [optional].
  1. Build the Go executable into a derivation.
  2. Version pin the Go compiler [optional].
  3. Build a Docker container with Nix with your package.

Follow the steps in examples/flake-go/README.md.

Outlook

Goodies 🍬 from SDSC

We maintain well-structured, state-of-the-art, nix-enabled repository templates for toolchains like:

The templates provide CI out of the box and are ready to use!

Your Nix Journey

  • 🌍 Tackling non-reproducible software distribution is hard — but essential

  • 🚀 Embrace Nix to gain

    • reproducibility
    • consistency
    • healthy local development
    • CI for free (when you fix something with  its fixed!)
  • 🤝 The Nix community is welcoming and supportive — don’t hesitate to ask!

  • ✅ Check the references/resources provided (🤷‍♀️ sometimes its a mess, but gets better).

References

Homework

Solutions in the notes.

Inspect the Dependency Graph

  • Reproduce the commands for building what-is-my-ip.nix on your machine in the workshop repository.
  • Inspect the store path.
  • Explore the dependencies and answer the quiz below:

Time: 15-20min

Quiz: What do you expect
/nix/store/zl7h70n70g5m57iw5pa8gqkxz6y0zfcf-curl-8.12.1-bin/bin/curl
links to and what does your system curl link to?

Use ldd to inspect.

Inspect a Flake

  • Load the flake in the the root directory in nix repl and use :lf .

    • Inspect the attribute inputs.nixpkgs.
    • Inspect the string "${inputs.nixpkgs}" and explore the output!
    • Try to explain import "${inputs.nixpkgs}" { system = "x86_64-linux"; }.
  • Eval/build/run the treefmt utility in the packages output in the flake inside the root directory.

    Hints:

    • nix eval --impure --expr 'builtins.currentSystem'
    • packages.${system}.treefmt
    • nix run
    • "github:sdsc-ordes/nix-workshop#..."

Time: 15-20min

Modify the DevShell

Time: 5 min

Appendix

Fixed Point Combinator 🤯

In maths a fix point x of a function F is defined as:

\[ x = F(x). \]

In functional programming a fix-point combinator fix is a higher-order function.
It returns the fix point of a function F:

fix = F: let x = F x in x

Fixed-Point Combinator 🤯

That is how recursive self-referential sets can be defined.

let
  fix = F: let x = F x in x;

  # Define the constructor of the set.
  newSet = self: { path = "/bin"; full = self.path + "/my-app"; };

  mySet = fix newSet; # fulfills: mySet == fix mySet;
in
  mySet.full

Seems recursive in let x = F x but but isn’t 🤯, because its lazy evaluated. What you need to know about laziness..

Used in pkgs.callPackage in nixpkgs.

Why Nix is Lazy Evaluated?

The choice for lazy evaluation allows us to write Nix expressions in a convenient and elegant style: Packages are described by Nix expressions and these Nix expressions can freely be passed around in a Nix program – as long as we do not access the contents of the package, no evaluation and thus no build will occur. […] At the call site of a function, we can supply all potential dependencies without having to worry that unneeded dependencies might be evaluated. For instance, the whole of the Nix packages collection is essentially one attribute set where each attribute maps to one package contained in the collection. It is very convenient that at this point, we do not have to deal with the administration of what exactly will be needed where. Another benefit is that we can easily store meta information with packages, such as name, version number, homepage, license, description and maintainer. Without any extra effort, we can access such meta-information without having to build the whole package. [Paper]

The builtin function derivation

This is what pkgs.writeShellScriptBin would expand to: (see ./examples/what-is-my-ip-orig.nix):

derivation {
  inherit system;

  name = "what-is-my-ip";
  builder = "/bin/sh";

  args = [
    "-c"
    ''
      ${pkgs.coreutils}/bin/mkdir -p $out/bin

      {
        echo '#!/bin/sh'
        echo '${pkgs.curl}/bin/curl -s http://ipinfo.io | \
        ${pkgs.jq}/bin/jq --raw-output .ip'
      } > $out/bin/what-is-my-ip

      ${pkgs.coreutils}/bin/chmod +x $out/bin/what-is-my-ip
    ''
  ];

  outputs = [ "out" ];
}

Store Derivation

A store derivation (*.drv) contains only build instructions for Nix to realize/build it. This can be literally anything, e.g. a software package, a wrapper shell script or only source files.

Evaluate & Build a Derivation

# Evaluate it.
drvPath=$(nix eval "./examples/flake-simple#packages.x86_64-linux.mytool" --raw)
# Realize it.
nix build -L "$drvPath" --print-out-paths --out-link ./mytool

Inspect a Derivation

nix eval "./examples/flake-simple#packages.x86_64-linux.mytool"

> «derivation /nix/store/l8pma77py04gd5819zkk3h7jx0bgxqgm-mytool.drv»

./examples/flake-simple#packages.x86_64-linux.mytool is an installable. More later!

# Inspect the store derivation.
cat /nix/store/l8pma77py04gd5819zkk3h7jx0bgxqgm-mytool.drv

Inspect a Derivation (2)

> Derive([("out","/nix/store/5rvqlxk2vx0hx1yk8qdll2l8l62pfn8n-treefmt","","")],
[("/nix/store/1fmb3b4cmr1bl1v6vgr8plw15rqw5jhf-treefmt.toml.drv",["out"]),
("/nix/store/3avbfsh9rjq8psqbbplv2da6dr679cib-treefmt-2.1.0.drv",["out"]),
("/nix/store/61fjldjpjn6n8b037xkvvrgjv4q8myhl-bash-5.2p37.drv",["out"]),
("/nix/store/gp6gh2jn0x7y7shdvvwxlza4r5bmh211-stdenv-linux.drv",["out"])]
,["/nix/store/v6x3cs394jgqfbi0a42pam708flxaphh-default-builder.sh"]
,"x86_64-linux","/nix/store/8vpg72ik2kgxfj05lc56hkqrdrfl8xi9-bash-5.2p37/bin/bash",
["-e","/nix/store/v6x3cs394jgqfbi0a42pam708flxaphh-default-builder.sh"],
[ ("__structuredAttrs",""),("allowSubstitutes",""),
("buildCommand","target=$out/bin/treefmt\nmkdir -p \"$(dirname \"$target\")\"\n\nif [ -e \"$textPath\" ]; then\n  mv \"$textPath\" \"$target\"\nelse\n  echo -n \"$text\" > \"$target\"\nfi\n\nif [ -n \"$executable\" ]; then\n  chmod +x \"$target\"\nfi\n\neval \"$checkPhase\"\n"),("buildInputs",""),("builder","/nix/store/8vpg72ik2kgxfj05lc56hkqrdrfl8xi9-bash-5.2p37/bin/bash"),("checkPhase","/nix/store/8vpg72ik2kgxfj05lc56hkqrdrfl8xi9-bash-5.2p37/bin/bash -n -O extglob \"$target\"\n"),("cmakeFlags",""),("configureFlags",""),("depsBuildBuild",""),("depsBuildBuildPropagated",""),("depsBuildTarget",""),("depsBuildTargetPropagated",""),("depsHostHost",""),("depsHostHostPropagated",""),("depsTargetTarget",""),("depsTargetTargetPropagated",""),("doCheck",""),("doInstallCheck",""),("enableParallelBuilding","1"),("enableParallelChecking","1"),("enableParallelInstalling","1"),("executable","1"),("mesonFlags",""),("name","treefmt"),("nativeBuildInputs",""),("out","/nix/store/5rvqlxk2vx0hx1yk8qdll2l8l62pfn8n-treefmt"),("outputs","out"),("passAsFile","buildCommand text"),("patches",""),("preferLocalBuild","1"),("propagatedBuildInputs",""),("propagatedNativeBuildInputs",""),("stdenv","/nix/store/hsxp8g7zdr6wxk1mp812g8nbzvajzn4w-stdenv-linux"),("strictDeps",""),("system","x86_64-linux"),("text","#!/nix/store/8vpg72ik2kgxfj05lc56hkqrdrfl8xi9-bash-5.2p37/bin/bash\nset -euo pipefail\nunset PRJ_ROOT\nexec /nix/store/0jcp33pgf85arjv3nbghws34mrmy7qq5-treefmt-2.1.0/bin/treefmt \\\n  --config-file=/nix/store/qk8rqccch6slk037dhnprryqwi8mv0xs-treefmt.toml \\\n  --tree-root-file=.git/config \\\n  \"$@\"\n\n")])

JSON output of the above:

nix derivation show /nix/store/l8pma77py04gd5819zkk3h7jx0bgxqgm-mytool.drv

Inspect a Derivation (3)

{
  "/nix/store/l8pma77py04gd5819zkk3h7jx0bgxqgm-mytool.drv": {
    "args": [
      "-e",
      "/nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh",
      "/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh"
    ],
    "builder": "/nix/store/9nw8b61s8lfdn8fkabxhbz0s775gjhbr-bash-5.2p37/bin/bash",
    "env": {
      "__structuredAttrs": "",
      "allowSubstitutes": "",
      "buildCommand": "target=$out/bin/mytool\nmkdir -p \"$(dirname \"$target\")\"\n\nif [ -e \"$textPath\" ]; then\n  mv \"$textPath\" \"$target\"\nelse\n  echo -n \"$text\" > \"$target\"\nfi\n\nif [ -n \"$executable\" ]; then\n  chmod +x \"$target\"\nfi\n\neval \"$checkPhase\"\n",
      "buildInputs": "",
      "builder": "/nix/store/9nw8b61s8lfdn8fkabxhbz0s775gjhbr-bash-5.2p37/bin/bash",
      "checkPhase": "/nix/store/9nw8b61s8lfdn8fkabxhbz0s775gjhbr-bash-5.2p37/bin/bash -n -O extglob \"$target\"\n",
      "cmakeFlags": "",
      "configureFlags": "",
      "depsBuildBuild": "",
      "depsBuildBuildPropagated": "",
      "depsBuildTarget": "",
      "depsBuildTargetPropagated": "",
      "depsHostHost": "",
      "depsHostHostPropagated": "",
      "depsTargetTarget": "",
      "depsTargetTargetPropagated": "",
      "doCheck": "",
      "doInstallCheck": "",
      "enableParallelBuilding": "1",
      "enableParallelChecking": "1",
      "enableParallelInstalling": "1",
      "executable": "1",
      "mesonFlags": "",
      "name": "mytool",
      "nativeBuildInputs": "",
      "out": "/nix/store/blm702jzcwfppwrrj9925ivd9gxp4c9n-mytool",
      "outputs": "out",
      "passAsFile": "buildCommand text",
      "patches": "",
      "preferLocalBuild": "1",
      "propagatedBuildInputs": "",
      "propagatedNativeBuildInputs": "",
      "stdenv": "/nix/store/npp9k9062ny7w0k1i03ij6xvqb7vhvjh-stdenv-linux",
      "strictDeps": "",
      "system": "x86_64-linux",
      "text": "#!/nix/store/9nw8b61s8lfdn8fkabxhbz0s775gjhbr-bash-5.2p37/bin/bash\n\"/nix/store/xkk1gr9bw2dbdjna8391rj1zl1l3dmhq-cowsay-3.8.4/bin/cowsay\" \"Hello there ;)\"\necho \"-------------------------------------\"\n\"/nix/store/4ydiim4lfk6nyab4pdkjj9s33pgbigfd-figlet-2.2.5/bin/figlet\" \"Do you expect\"\n\"/nix/store/4ydiim4lfk6nyab4pdkjj9s33pgbigfd-figlet-2.2.5/bin/figlet\" \"something \"\n\"/nix/store/4ydiim4lfk6nyab4pdkjj9s33pgbigfd-figlet-2.2.5/bin/figlet\" \"useful ? \"\n\n"
    },
    "inputDrvs": {
      "/nix/store/1fsd2cb5ab7ci01ks4j0gbbq254jw6sk-stdenv-linux.drv": {
        "dynamicOutputs": {},
        "outputs": ["out"]
      },
      "/nix/store/lrf9kbhlaf5mkvnlf3zr9wzvk7c2z72l-bash-5.2p37.drv": {
        "dynamicOutputs": {},
        "outputs": ["out"]
      },
      "/nix/store/phq4wh4490manblg905xixpc3gvwr149-figlet-2.2.5.drv": {
        "dynamicOutputs": {},
        "outputs": ["out"]
      },
      "/nix/store/wdpicivrj0bmzh935rr1hm1vlk18j0mp-cowsay-3.8.4.drv": {
        "dynamicOutputs": {},
        "outputs": ["out"]
      }
    },
    "inputSrcs": [
      "/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh",
      "/nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh"
    ],
    "name": "mytool",
    "outputs": {
      "out": {
        "path": "/nix/store/blm702jzcwfppwrrj9925ivd9gxp4c9n-mytool"
      }
    },
    "system": "x86_64-linux"
  }
}

What Is an Installable

The path ./examples/flake-simple#packages.x86_64-linux.mytool is referred to as a Flake output attribute installable, or simply an installable.

An installable is a Flake output that can be realized in the Nix store.

  • ./examples/flake-simple refers to this repository’s flake.nix directory.
  • packages.x86_64-linux.mytool following # is an output attribute defined within the flake.

Most modern Nix commands accept installables as input, making them a fundamental concept in working with Flakes. You should only use the modern commands, e.g. nix <subcommand>. Stay away from the command nix-env.