Open-source project
input-output-hk/haskell.nix avatar
input-output-hk/haskell.nix

haskell.nix: building Haskell packages reproducibly with Nix

Alternative Haskell Infrastructure for Nixpkgs

634 stars268 forksNixApache-2.0

At a glance

What is it?
haskell.nix is an alternative Haskell infrastructure for nixpkgs that takes a Cabal or Stack project and translates it, together with its dependencies, into Nix code. It ships its own compiler builds across a wide range of host and target platforms, publishes them to binary caches so nobody has to build GHC, and separates the runtime system from the two data sources it consumes.
Who is it for?
haskell.nix fits a Haskell team that wants reproducible, hermetic builds with cross-compilation to many targets, and a team that has hit the point where the nixpkgs Haskell infrastructure no longer matches what they need. It does not fit a project that only builds for its own platform, because the machinery costs more than it returns there.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Nix, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the project is, in two sentences

The readme opens with the definition and the headline capability, and both are short enough to quote.

haskell.nix is infrastructure for building Haskell packages with Nix, and it can automatically translate your Cabal or Stack project and its dependencies into Nix code.

That second sentence is the whole pitch. The Haskell ecosystem has two ways of describing a build, Cabal and Stack, and the conventional way to use Nix with them is to write the Nix expression by hand and to keep it in step with the manifest as the dependencies change. This project does the translation instead, which means a new dependency in the Cabal file becomes a new Nix derivation without a human editing anything.

The context matters. nixpkgs has a Haskell infrastructure of its own, and this is an alternative to it rather than a fork of it, which means the two can evolve separately and a project can choose. The topics the repository carries include build tool, infrastructure, Nix, Nix expressions, and Haskell, and the presence of a hacktoberfest topic explains the shape of the contribution history.

The licence is Apache 2.0, the documentation site is input-output-hk.github.io/haskell.nix, and the last push was on 2026-10-20.

The documentation is organised as a book rather than as a wiki, with an introduction, a getting started tutorial, and a troubleshooting page that the readme links from two separate places, which is a hint about where readers actually get stuck.

The one failure everybody hits

There is a section of the readme headed with a plea for help, and it is about a single problem, which tells you how much of the user experience this project is about.

The number one problem people have when using haskell.nix is that they find themselves building GHC. This should not happen, but you must follow the setup instructions properly to avoid it. If this happens, check the getting started instructions about setting up the binary cache, and then consult the corresponding troubleshooting section, which is titled with the question of why am I building GHC.

The failure is worth understanding rather than memorising. GHC is a compiler with a bootstrap process, where each version is built by the previous one, and building it from source takes a long time and a lot of disk. A user who ends up doing that has not misconfigured a flag, they have skipped the step that points the build at a cache, and the symptom is a build that appears to hang for an hour and then succeed.

So the readme puts the warning in the position where a user will see it, names the cause, and links to both the instruction and the troubleshooting entry. And it closes with the observation that the troubleshooting documentation contains help for other common issues, and that if you are still stuck, open an issue.

That structure is what a mature infrastructure project looks like. The interesting failures are known, named, and documented, and the documentation is organised around the failure rather than around the feature.

The binary caches, and the status table

The readme publishes prebuilt GHCs, and boot libraries, to two binary caches, and the distinction between them is the useful part.

The first, cache.iog.io, only holds the GHC versions currently in use by one organisation's own projects, so it does not cover every compiler the project supports. The second, cache.zw3rk.com, holds all of the compiler versions currently tested in the project's own continuous integration, including cross-compilers, and configuring it as an extra substituter is described as the surest way to avoid building GHC yourself.

The reason there are two is worth naming. A cache that is scoped to one organisation's needs will be small, fast to serve, and always current for that organisation, and it will be missing the versions and targets that only somebody else needs. A cache scoped to everything the project tests is large and general. Running both is what makes the difference in practice irrelevant, since a substituter that does not have a path is simply not consulted for it.

The status table is the other half of the story, and it is generated rather than written. The readme says the table is generated automatically from the latest master builds on a continuous integration job set, and shows the build status of GHC itself, the roots job, for each compiler and platform. A check mark means that compiler and platform is built and cached for the current master, and the readme makes a careful point about it: a build only counts once it is part of the latest evaluation, so a green never reflects a stale build.

That last clause is the one that matters. In a project that caches compilers, a green status from three weeks ago tells you very little, because the cache may have been invalidated since. Tying the status to the current evaluation is what makes the table a live answer rather than a historical record.

The table itself shows how wide the support is. There are seven compiler columns, from a 9.14 series compiler through to a 9.14 series compiler with a LLVM backend, and the rows are host and target pairs: Linux on x86_64 as a host, with targets including static linking, musl on 64 and 32 bit, aarch64, aarch64 with musl, Android on aarch64 and armv7a, JavaScript, WebAssembly, and two Windows targets. Then aarch64 Darwin and x86_64 Darwin as hosts, with their own cross targets. The Nixpkgs column distinguishes the unstable channel from a release, which is the point at which the project's support is tested against a pinned channel as well as a moving one.

And the table has holes, which is the honest part. The Darwin rows are largely empty, and so are the release channel rows. Reading it tells you where the project is strong and where it is not, which is more useful than a claim of universal support.

The runtime, and the two data sources it consumes

The related repositories section explains the decomposition, and it is a clean one.

The haskell.nix repository contains the runtime system for building Haskell packages in Nix. It depends on two other repositories, and the readme says so directly. The first is hackage.nix, which holds the latest contents of the Hackage package databases converted into Nix expressions. The second is stackage.nix, which holds all of the Stackage snapshots converted into Nix expressions.

The split is the interesting engineering decision. The runtime, the part that knows how to compile a Haskell package and how to cross-compile it, changes on the scale of the project. The data, the package index and the curated snapshot sets, change every day, because the package index changes every day and a snapshot is a moving target too. Splitting them means the data can be regenerated on a schedule and the runtime does not have to be re-evaluated when it does.

It also means the two data sources are interchangeable, which is the practical consequence for a user. A project that resolves its dependencies from Hackage with a Cabal file and a project that resolves them from a Stackage snapshot with a Stack file are using the same runtime and the same derivation machinery, and the difference between them is which of the two data repositories the evaluation reads.

The repository tree shows the same structure at the level of the source. There is a builder directory, a compiler directory, a hix directory, a lib directory, a package set file, a snapshots file, a config file, overlays for the cross-compilation targets, modules for the Cabal project handling, patches, templates for new projects, a local Hackage repository helper, a materialized directory, and the test directory. There is also a builder helper written in another language, a per-system static tools file, and a build file for the project itself.

The end of the readme adds a note that is out of place among the technical content and is worth repeating: for commercial support, there is an address to reach. Infrastructure of this kind, maintained by a small group, is often funded by the organisations that depend on it, and the readme is upfront about that.

What the build system checks

The make file in the repository is worth reading, because a verification target for a project of this kind is a good description of what it considers its own correctness.

The default goal is help, the python interpreter is configurable, there is a timeout, and two settings are the ones a contributor is most likely to change: the compiler to test against, and the system to test on. The test suite is selected by a variable as well.

The check target is described in a comment as checking whitespace, Nix syntax, emulator selection, and dependencies. The target line itself reads: check: ## Check whitespace, Nix syntax, emulator selection, and dependencies\n\nThe commands behind it are the ones you would expect from that description. A diff check for whitespace problems, and then a series of parses of individual Nix files: the compiler builder, the Cabal slice builder, the stage Cabal file builder, the Cabal overrides test, the Windows basement test, the cross-compilation overlays for Linux, for armv6l, for the qemu user mode emulation, and the main overlay, plus the default test, the annotation test, the Cabal project module, and the stable runtime test.

Two of those are not simple parses. One evaluates a test expression with the flakes feature enabled and in strict mode, which is a stronger check than parsing because it forces the evaluation. Another evaluates a test file in strict mode, which checks that the qemu selection logic actually resolves.

The target list in the file is the real specification of the project, and it reads like a list of the things that have historically broken. There is a Linux cross wrapper test, two variants of the Cabal overrides test for the second version of the builder, two Windows basement tests, a mingw standard IO test, a qemu test for the Linux user mode, another for the arm32 personality, tests for the ELF comdat functions, and two probe tests for the Rosetta signals, which is the mechanism by which x86 binaries run on Apple hardware. A verify target sits at the end of the list.

The Rosetta probes are a good example of the kind of thing only a real infrastructure project accumulates. A build that works on one architecture and fails on another, with a specific and obscure failure, is the kind of bug that only shows up when somebody tries to build for the other architecture, and the fix is to have a test that fails the same way.

The Makefile also declares a large set of targets as phony, which is the correct hygiene for a file whose targets are mostly file names that do not exist.

A project that keeps its own versions

The release history in the repository metadata is worth a close look, because it shows a project whose visible releases are not the project itself.

The three most recent tags are all versions of one small component, nix-tools-sh, at 0.2.1, 0.2.2, and 0.2.3, in August, September, and late September of 2026. There are no releases for haskell.nix itself in that list.

The tree explains why. There is a directory called nix-tools, a directory called nix-tools-sh, and two files that pin static versions of the tools for different setups, one of them explicitly for the default setup and one for the shell variant. The tools being released separately are the helper binaries, and versioning them on their own lets them be updated without touching the infrastructure, which is the same decomposition logic as the split between the runtime and the package data.

The rest of the top level tells you the other half of the story. There is a flake with a lock file, which is how a modern Nix project declares its inputs. There is an environment file for the directory environment tool. There is a book configuration file, matching the documentation being a book. There is a changelog file and a bug log, and a separate file for the release process, plus one for the Linux-only release. There are agent instruction files, which is a convention that has become common in repositories of this kind.

And there is a theme directory, which is for the documentation site, and a docs directory, and a shell file that presumably provides a development environment.

Two things are worth drawing out of that. The first is that the project keeps a bug log as well as a changelog, which is a sign of a project that records the failures it has had as carefully as the features it has shipped. The second is the presence of a directory for the local Hackage repository helper, which suggests the project can host packages that are not on the public index, which is a requirement for any organisation with internal Haskell packages.

The overall picture is of an infrastructure project that treats its own maintenance as a first-class concern: a book for the documentation, generated status tables, a verification target that checks the Nix files individually, and a release process split so that the parts that change often are released separately from the parts that do not.

Editorial conclusion

haskell.nix fits a Haskell team that wants reproducible, hermetic builds with cross-compilation to many targets, and a team that has hit the point where the nixpkgs Haskell infrastructure no longer matches what they need. It does not fit a project that only builds for its own platform, because the machinery costs more than it returns there. Verify first that your compiler version and your target platform are in the cached set, because the table in the readme is the authoritative list of what is already built, that you have configured the second binary cache as a substituter, since the first one only holds the versions one organisation's own projects use, and that you have read the getting started instructions, because the single most common failure is ending up building GHC yourself, which the project says should not happen.

Frequently asked questions

What does haskell.nix do?

It is an alternative Haskell infrastructure for nixpkgs that can automatically translate a Cabal or Stack project and its dependencies into Nix code, so that a change in the manifest becomes a change in the build without editing expressions by hand.

Why am I building GHC when I use haskell.nix?

Because the setup step that points the build at a binary cache was missed. The readme names this as the single most common problem and says it should not happen, and points to the getting started instructions about the binary cache and to the troubleshooting section titled with the question of why am I building GHC.

Which binary caches does haskell.nix publish to?

Two. One holds only the compiler versions currently in use by one organisation's own projects, so it does not cover everything, and the other holds all of the compiler versions currently tested in the project's continuous integration, including cross-compilers, and is described as the surest way to avoid building GHC yourself.

Which platforms does haskell.nix support?

The status table in the readme is the authoritative list, and it covers seven compiler versions across Linux, Darwin, Android, JavaScript, WebAssembly, and Windows targets, including static and musl variants. The table is generated from the latest continuous integration builds, and a green entry means the build is part of the current evaluation rather than a stale one.

What is the relationship between haskell.nix, hackage.nix, and stackage.nix?

The haskell.nix repository contains the runtime system for building Haskell packages in Nix, and it depends on the other two, which hold the latest Hackage package database contents and the Stackage snapshots, converted into Nix expressions. The runtime changes on a different schedule from the data, which is the reason for the split.

Official sources

  1. input-output-hk/haskell.nix on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/input-output-hk-haskell-nix.svg)](https://hysenlabs.com/projects/input-output-hk-haskell-nix)

Was this useful?