# nixpkgs-multiverse: every nixpkgs version from one flake input

> nixpkgs-multiverse turns 1,551 nixpkgs revisions into a single flake input, so you can run Python 3.6.2 or any other historical package version without juggling pinned nixpkgs commits. The index is broad, the fast path depends on Hydra's cache, and the design has real edges.

**fzakaria/nixpkgs-multiverse** — Any version of any nixpkgs package, from one flake input

- Repository: https://github.com/fzakaria/nixpkgs-multiverse
- Website: https://nixmultiverse.com/
- Stars: 491 · Forks: 12
- Language: Python
- License: MIT
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/fzakaria-nixpkgs-multiverse

## What nixpkgs-multiverse is for

Nixpkgs keeps one version of each package at a time. If you need Python 3.6.2, or the OpenSSL that shipped three years ago, the usual answer is to find the nixpkgs commit where that version lived and pin it as a separate flake input. Do that for several packages and you are maintaining a small matrix of nixpkgs revisions, each with its own evaluation cost and its own set of unrelated changes.

nixpkgs-multiverse collapses that matrix into one input. The README describes it as "Every nixpkgs revision, reachable from a single evaluation. One flake input, no juggling nixpkgs pinned at N commits." The audience is people who already work in flakes: developers reproducing an old toolchain, maintainers testing an upgrade across versions, and anyone writing a NixOS or home-manager configuration that has to hold a package at a specific release. The index covers 2012-07-05 through 2026-09-14, and the README reports 311,781 package versions across 32,208 attributes from 1,551 revisions.

The project is MIT licensed and written mostly in Python. The repository separates the Nix expressions from the generated metadata: revisions.json, index/versions.json and index/history.json are described in the README as generated metadata about nixpkgs (revisions, dates, hashes and version strings), not nixpkgs source. That distinction matters if you are auditing what you pull in.

## How the index and the two evaluation paths work

The mechanism is an index, not a fork of nixpkgs. A flake input exposes attributes under namespaces: versions.<attr>."<version>" for an exact version, latest.<attr> for the newest one, and a revision selector such as tip, a release channel like 26.05, or a commit hash. Selecting a version does not fetch anything by itself; the README states that enumerating versions reads an index file, and that a revision is materialised the first time you force a derivation.

There are two ways to materialise it. The default path fetches and evaluates a nixpkgs tree, which the README sizes at roughly 378 MB on first use. The fast path skips fetching and evaluation entirely. The store-path index already records the path Hydra built, so Nix substitutes it from cache.nixos.org. The README credits the technique to tomberek's fastpkgs project. Fast attributes are prefixed with fast. and require appending the output name, typically .out, because the fake derivation has nothing to build, only a path to fetch.

That split is the core design decision, and it is also the main constraint. The fast path is fast only when Hydra built and cached that exact store path. The README does not claim coverage for every one of the 311,781 versions. When a path is missing, you fall back to fetching and evaluating the tree, which is the minutes-long case the fast path exists to avoid.

## Installing nixpkgs-multiverse and running a first package

There is no install step in the usual sense. The flake is consumed directly from GitHub, so the commands in the README run against github:fzakaria/nixpkgs-multiverse. The first command runs Python 3.6.2 and prints its version. Expect a delay: the README says this fetches and evaluates a roughly 378 MB nixpkgs tree the first time.

```bash
nix run 'github:fzakaria/nixpkgs-multiverse#versions.python3."3.6.2"' -- --version
```

The second command is the fast variant of the same idea. Note the fast. prefix and the .out suffix, which the README says are required because the fake derivation only has a path to fetch. On a cache hit this substitutes from cache.nixos.org instead of evaluating nixpkgs.

```bash
nix shell 'github:fzakaria/nixpkgs-multiverse#fast.versions.python3."3.8.9".out'
```

To see what versions exist before committing to one, the bundled mvs tool answers queries offline from a database baked into the flake. The README's example output lists each version with its first and last appearance and a revision count.

```bash
nix run github:fzakaria/nixpkgs-multiverse#mvs -- query versions python3
```

For a declarative configuration, the flake ships a module. The README shows the NixOS form, and notes that nix-darwin uses darwinModules.default while home-manager uses homeManagerModules.default. Pins are attribute names mapped to version strings.

```nix
{
  imports = [ inputs.multiverse.nixosModules.default ];

  multiverse.enable = true;
  multiverse.pins.python3 = "3.8.9";
}
```

## Where the fast path stops being fast

The store-path index is the feature that makes nixpkgs-multiverse practical, and it is the feature with the sharpest boundary. A store path is only substitutable if Hydra built it and cache.nixos.org still holds it. The README presents the fast path as the way to get "seconds instead of minutes," but it does not state that every indexed version has a corresponding cached path.

When the path is absent, you are back on the default route: fetch and evaluate the nixpkgs tree. That is not a bug, but it changes the cost profile of a build. A CI job that pins five old packages and assumes substitution will be slow, or will fail on a machine without network access to the cache, in exactly the cases where the fast path did not apply. There is also a correctness trap in the attribute shape: fast attributes need the .out suffix. Dropping it is a user error the README warns about implicitly by showing it in every fast example, not by describing what happens if you omit it.

The other boundary is scope. This is a metadata index over nixpkgs, not a compatibility layer. A pinned version is the package as nixpkgs built it at that revision, with that revision's dependencies and patches. If your goal is to run old software against a current system library set, nixpkgs-multiverse does not reconcile that for you; it gives you the old derivation.

## Compared with pinning nixpkgs directly

The obvious alternative is what people did before: add a second nixpkgs input pinned to a commit, and reference packages from it. That approach is exact and needs no third-party index. Its cost is multiplication. Each pinned nixpkgs is a full tree to fetch and evaluate, and a configuration that needs three historical versions carries three trees plus the current one. The README frames nixpkgs-multiverse as the answer to that, describing the status quo as juggling nixpkgs pinned at N commits.

The difference in approach is that nixpkgs-multiverse replaces N inputs with one input plus a generated index. You trade control over the exact revision for a curated set of 1,551 revisions and a naming scheme that addresses versions directly. If the version you need falls between two indexed revisions, or if you need a revision the index does not include, a hand-pinned nixpkgs input is still the more direct tool. The project also documents replacing nixpkgs inputs and a non-flake path, so it is not strictly a flakes-only story, but the quickstart is flake-first.

## Maintenance, releases and licence

The repository is not archived, and the last push was on 2026-09-16. Releases are dated data artifacts rather than versioned software: data-20260916, data-20260914 and data-20260912, each described as store-path artifacts. That naming tells you what to expect from upgrades. The tooling changes rarely; the index is refreshed as new nixpkgs revisions appear, and the README's status block is generated between markers, showing the newest indexed revision and its release channel.

Upgrade cost is therefore mostly index churn, not code migration. Pulling a newer flake input can change which versions exist and which store paths are available, so a pin that resolves today may resolve differently after a refresh if the underlying revision set moves. The module pins are version strings, which is a stable interface, but the set of versions behind those strings is not frozen. The README does not document rollback of the index itself; if you need reproducibility across index updates, pin the flake input to a specific revision the way you would any other flake.

On licensing: the project is MIT. The README states that the Nix expressions and tooling are original work, that revisions.json, index/versions.json and index/history.json are generated metadata rather than nixpkgs source, and that nixpkgs itself is MIT and fetched at evaluation time. That is the project's own description, not a legal opinion, and anyone redistributing the metadata or the fetched nixpkgs should read the LICENSE and nixpkgs' own terms.

## Conclusion

Adopt nixpkgs-multiverse if you need to run or pin a package version that current nixpkgs no longer carries, and you are comfortable with flakes and a large first evaluation. Do not adopt it if you only ever need the newest nixpkgs, or if you need a version whose Hydra build was never cached and cannot afford to build it. Before relying on a specific version, verify that it appears in the mvs query output and that the fast path resolves to a store path, because the slow path fetches and evaluates a roughly 378 MB nixpkgs tree.

## FAQ

### What is the NixOS distribution?

The README does not describe NixOS as a distribution, so this article cannot answer it. What it does cover is nixpkgs-multiverse, an index over nixpkgs revisions, which is a separate project.

### How do I run an old version of a package with nixpkgs-multiverse?

Use the versions namespace with the attribute and quoted version string, as in the README's example for Python 3.6.2. The first run fetches and evaluates a roughly 378 MB nixpkgs tree, while the fast. variant substitutes a cached store path instead.

### What does the mvs CLI do in nixpkgs-multiverse?

It answers version questions offline from a database baked into the flake. The README's example is mvs query versions python3, which lists each version with its first and last appearance and a revision count.

### Can I pin a package version in a NixOS configuration with nixpkgs-multiverse?

Yes. The README shows importing inputs.multiverse.nixosModules.default, setting multiverse.enable to true, and assigning a version string such as multiverse.pins.python3 = "3.8.9". It notes that nix-darwin uses darwinModules.default and home-manager uses homeManagerModules.default.

## Sources

- [fzakaria/nixpkgs-multiverse on GitHub](https://github.com/fzakaria/nixpkgs-multiverse)
- [License: MIT](https://github.com/fzakaria/nixpkgs-multiverse/blob/main/LICENSE)
- [Project website](https://nixmultiverse.com/)
- [README](https://github.com/fzakaria/nixpkgs-multiverse/blob/main/README.md)
- [Releases](https://github.com/fzakaria/nixpkgs-multiverse/releases)

---

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