# nix-tree: a TUI for finding out why a Nix store path is so large

> nix-tree is a Haskell terminal program that turns a Nix store path into an interactive dependency graph, with the three sizes that matter separated out, a why-depends view, and a --store option that lets you query a binary cache without downloading anything. It has been in nixpkgs since 20.09 and is a single cabal package.

**utdemir/nix-tree** — Interactively browse dependency graphs of Nix derivations.

- Repository: https://github.com/utdemir/nix-tree
- Stars: 1,085 · Forks: 22
- Language: Haskell
- License: BSD-3-Clause
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/utdemir-nix-tree

## A dependency graph viewer that answers the size question

nix-tree is described in one line as a program to interactively browse dependency graphs of Nix derivations, and it is written in Haskell under a BSD-3-Clause licence. The build badge points at a build workflow, and a packaging badge points at Repology across the various repositories.

The framing of the tool is worth noticing. It is not a tool for building anything, and it does not replace nix why-depends. It is a viewer, and the thing it is designed to make legible is the size of a store path. A Nix user who has just run a build and seen an unexpectedly large download is asking a specific question, and the README's glossary is built around answering it.

Three sizes are defined, and the distinctions between them are the reason the tool exists. NAR size is the size of the store path itself. Closure size is the total size of the store path and all its transitive dependencies. Added size is the size of the store path and all its unique transitive dependencies, described in the README as the cost of having that store path on top of all other paths, with a pointer to issue #14 for a fuller explanation.

That third figure is the one that matters for a disk-space question, and it is also the one that is hard to compute by hand. The closure of a package with hundreds of dependencies is a total, and the total tells you nothing about which dependency is responsible. The added size of each node in the graph isolates what each one contributes, which is exactly the shape of the question someone asks when the store is filling up.

The program runs as a terminal interface, and the keybindings are listed in the usage output. hjkl and the arrow keys navigate, w opens the why-depends modal, / opens a search modal, s changes the sort order, y yanks the selected path to the clipboard, ? shows help, and q or Esc quits or closes a modal.

## The why-depends modal is the feature that turns a viewer into an investigation tool

A dependency graph tells you what depends on what. It does not tell you why a particular edge exists, and in a Nix store the edges come from many different mechanisms: a build input, a propagated build input, a runtime reference, a patch, a propagated native build input. When you are looking at a graph of several hundred nodes, the question that matters is always about one specific edge.

The why-depends modal exists for that. The w key opens it for the selected node, and it is a modal overlay rather than a separate command, so the answer appears in the context of the graph you are already looking at. The README does not describe the exact output format of that modal, so what it shows and how it presents the chain is something to find out by running it.

The search modal, opened with /, is the other half. In a graph with hundreds or thousands of nodes, scrolling is not a way to find a package. Searching for a name and jumping to it is what makes the tool usable at that size, and the fact that both features exist as keybindings in a single-letter scheme suggests the author expects the interface to be used fluently rather than visited occasionally.

The remaining bindings are small conveniences that reveal the use case. The s key changes sort order, which is how you switch between sorting by name, by size and by something else, and given that the whole point of the tool is comparing sizes, sorting by size is the mode that will be used most. The y key yanks the selected path to the clipboard, which is the bridge between the graph and the shell: you find the path that is responsible, copy it, and then investigate it with nix commands, or paste it into a garbage collection root analysis.

The clipboard binding also tells you the tool is meant to be used alongside the normal Nix command line rather than instead of it.

## Querying a store without downloading is what the --store option is for

The most useful trick in the README is the one that has nothing to do with the interface.

The --store option sets the URL of the Nix store, and the help text gives the examples daemon and https://cache.nixos.org, pointing at nix help-stores for the supported store types. The README then shows what that enables:

```bash
nix eval --raw 'nixpkgs#stellarium.outPath' | xargs -o nix-tree --store https://cache.nixos.org
```

The comment above it is the point: query the runtime dependency of stellarium, a 2 GiB closure, without download. A binary cache is a read-only store, and querying it tells you the closure size and the dependency graph of a package you have never installed, without pulling two gigabytes across the network. The nix eval call produces the store path without building anything, and nix-tree asks the cache what that path depends on.

The same idea works for a local store, and the README gives a second example:

```bash
nix build --store /tmp/chroot-store 'nixpkgs#hello' --print-out-paths | xargs -o nix-tree --store /tmp/chroot-store
```

That one builds in a temporary chroot store and examines the output, which is how you inspect a build without touching the real store at all.

The rest of the store-related flags follow the same logic. The --impure flag allows access to mutable paths and repositories, which is the usual Nix purity escape hatch for a store that is not a pure derivation environment. And the --file flag interprets installables as attribute paths relative to the Nix expression stored in a file, so you can point the tool at a specific expression rather than at a flake reference.

Between them, these turn the tool from an inspector of what you already have into a way of planning what you are about to build.

## Piping into it is the normal way to use it

The tips section is the best practical part of the README, because it shows the tool as a filter rather than an application you visit.

nix-build prints built paths to stdout, which can be piped conveniently with | xargs -o nix-tree. The examples cover the four cases you will actually hit:

```bash
nix-build . --no-out-link | xargs -o nix-tree
nix-instantiate . | xargs -o nix-tree --derivation
nix-build shell.nix -A inputDerivation | xargs -o nix-tree
nix-build '<nixpkgs>' -A openssl.all --no-out-link | xargs -o nix-tree
```

The first is the output of a local derivation, the second is build time dependencies by passing a .drv path, the third is dependencies from a shell.nix, and the fourth is all outputs of a derivation in nixpkgs. The --derivation flag is the one that changes the question being asked, since the help text says it operates on the store derivation rather than its outputs, and build time dependencies are usually a different and larger set than runtime ones.

Flake references are supported too:

```bash
nix-tree --derivation '.#'
nix-tree --derivation 'nixpkgs#asciiquarium'
```

The first is the build time dependencies of a flake in the current directory, and the second shows that any flake reference works, not just a local one.

For a whole system there is a dedicated form, nix-tree /nix/var/nix/profiles/system to run it on the current nixos system, and for a flake-based configuration the README shows building the toplevel first with nix build --print-out-paths --no-link and then pointing nix-tree at '.#nixosConfigurations.foo.config.system.build.toplevel'. The two-step form is needed because the graph is only meaningful once the system has been built.

The xargs -o flag appears in every one of these commands, and the README notes that nix-build prints built paths to stdout, which can be piped conveniently with | xargs -o nix-tree. The flag is what makes the piped paths land as the arguments of nix-tree.

## The install path is nixpkgs, and the dev path is a nix-shell with cabal

The installation section is short because the tool has been in nixpkgs since 20.09, and the README says to use your preferred method for adding packages to your system.

```bash
nix-env -iA nix-tree
```

That is the classic approach, and for flake enabled systems the README gives the profile form:

```bash
nix profile install 'nixpkgs#nix-tree'
```

For the current development version there is a one-liner that needs no clone and no build directory:

```bash
nix run github:utdemir/nix-tree -- --help
```

That last form is the most useful one for a first look, because it fetches the current tree, builds it in a temporary store and runs it, so you can see the interface before deciding whether the tool belongs on your machine.

The repository itself is a conventional Haskell project. There is nix-tree.cabal, a default.nix and a flake.nix with a flake.lock, a shell.nix, a src/ directory, an app/ directory and a test/ directory, alongside a CHANGELOG.md and a format.sh script. The build and packaging are described with both Nix and Cabal, so the project is buildable either way.

Contributing is described in one sentence: run nix-shell and use cabal as usual, and run ./format.sh before sending a PR. The format script is what keeps a Haskell codebase consistent, and the fact that the repository ships it as a script rather than asking contributors to install a formatter separately is a small kindness.

The tool has no GitHub releases, so its version comes from the nixpkgs packaging or from the changelog, and the last push was on 2026-10-01.

## Where nix-tree is the wrong tool

The limits are narrow, which is what makes the tool pleasant to use, but they are real.

The first is that it needs a Nix store to talk to. It is a viewer of Nix derivations, and there is no mode in which it inspects a Docker image, a conda environment or a set of installed system libraries. The related tools listed at the end of the README are all Nix-specific in the same way, with nix-du for visualising which garbage collection roots to delete, nix-melt as a ranger-like flake.lock viewer, nix-query-tree-viewer as a GTK viewer for the output of nix-store --query --tree, and nix-visualize for the dependencies of a given package.

That last list also marks the boundaries. nix-du covers the disk space question from the other end, asking which roots to delete rather than which dependencies are heavy, and nix-query-tree-viewer covers the case where you want a tree view of a store path without the interactive size analysis. nix-tree is the interactive one.

The second limitation is scale. The tool is designed for the store paths of a package and its transitive dependencies. Point it at a whole NixOS system closure and you have a graph with thousands of nodes, and while the search modal helps, the added size column is only useful if you can sort by it and then work down the list. A graph that large is a job for a query, not a browsing session.

The third is that the tool tells you what is there, not what to do. It can show you that a package pulls in a large dependency and it can show you the why-depends chain for the edge, but it does not propose a substitution, and it does not know that some other package would have provided the same library more cheaply. That judgement is the user's, and the clipboard binding exists so you can leave the tool and do it.

The fourth is the store requirement for the interesting features. Querying a binary cache without downloading depends on a reachable cache with the metadata you need, and the example uses cache.nixos.org. A private cache that only serves some paths, or a store on a machine that cannot reach the cache, will not answer the same questions.

## Conclusion

nix-tree is worth installing on any machine that manages software with Nix, because the question it answers, why is this store path the size it is and what pulls that dependency in, is otherwise a manual exercise with nix why-depends and a lot of guessing. It is the wrong tool if you are not using Nix, and the wrong tool if your problem is which garbage collection root to delete, which is what the related nix-du tool is for. Before relying on it, read the glossary in the README, because the difference between NAR size, closure size and added size is the whole interface, and check the --store option against the binary cache you use, since the example in the documentation queries cache.nixos.org.

## FAQ

### How do I install nix-tree?

It is in nixpkgs since 20.09, so you can use nix-env -iA nix-tree, or nix profile install 'nixpkgs#nix-tree' on flake enabled systems. The current development version can be run without installing with nix run github:utdemir/nix-tree -- --help.

### What is the difference between NAR size, closure size and added size in nix-tree?

NAR size is the size of the store path itself, closure size is the total of the store path and all its transitive dependencies, and added size is the size of the store path plus its unique transitive dependencies, which is the cost of having that path on top of all other paths.

### How do I use nix-tree on a package I have not downloaded?

Use the --store option to point it at a read-only store such as the binary cache, for example nix eval --raw 'nixpkgs#stellarium.outPath' | xargs -o nix-tree --store https://cache.nixos.org, which shows the dependency graph and closure size without downloading the package.

### What keys does nix-tree use?

hjkl and the arrow keys navigate, w opens the why-depends modal, / opens the search modal, s changes the sort order, y yanks the selected path to the clipboard, ? shows help, and q or Esc quits or closes a modal.

### How do I see build time dependencies instead of runtime dependencies?

Pass the --derivation flag, which operates on the store derivation rather than its outputs. With a flake reference that is nix-tree --derivation '.#' for the current directory, or nix-tree --derivation 'nixpkgs#asciiquarium' for any flake reference.

## Sources

- [Issues](https://github.com/utdemir/nix-tree/issues)
- [License: BSD-3-Clause](https://github.com/utdemir/nix-tree/blob/main/LICENSE)
- [README](https://github.com/utdemir/nix-tree/blob/main/README.md)
- [utdemir/nix-tree on GitHub](https://github.com/utdemir/nix-tree)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/utdemir-nix-tree
