This blog post was inspired by the work of Gabriel Gonzalez at haskell-nix github repository which has a lot of practical information on how to structure a Haskell project in NixOS. Here I will show my own view on a typical Haskell project structure in NixOS taking into account Gabriel’s work.
I would encourage everyone interested to fork haskell-nix and go through Gabriel’s tutorial. That helped me understand some of the issues I was not aware before.
default.nixmust not change other than with
$ cabal2nix . > default.nix
shell.nixmust be able to bring developer environment with
Hoogleserver that will serve documentation for all dependencies used in Cabal file
release.nixmust declare at least two packages - one with dynamically linked dependencies, and another - with statically linked dependencies
release.nixmust produce output expected by Hydra build system
shell.nixas well as
release.nixmust use pinned
nixpkgsto ensure reproducibility of builds
shell.nixas well as
release.nixmust be open for choosing version of
This is mostly straightforward:
To ensure reproducible builds it is important to fixate on a specific version of
nixpkgs. Let’s now generate
nixpkgs.json that will be used later in
release.nix by issuing the following command:
This should produce the following output:
It is possible to generate
$ cabal2nix --shell . > shell.nix, but it is not recommended as we are going to make sure we meet the requirement of running a Hoogle server locally with all the used dependencies.
Then in order to start your local Hoogle server:
Then navigate to 127.0.0.1:8080 in order to test if documentation server is running.
release.nix also needs to use pinned version of
nixpkgs. It also declares
two haskell packages - one
package-name and another
dynamically and statically linked versions respectively. It also produces a set
of derivations as a result which is what Hydra
Make sure to rename
package-namewith your project name.
Then building the project locally ends up being as simple as:
And installing dynamically linked package with
$ nix-env -i ./result. Or
statically linked package with
$ nix-env -i ./result-2.
Make sure you understand that
package-name-static package is statically linked
against current version of
glibc used in the current system. This has some
consequences that are not necessarily obvious. For example it will not
necessarily work when packaged as
alpine docker image. Reason being that
alpine images are built using
musl C-library instead of
glibc, and since
glibc statically linked it won’t be possible to
always properly run it. The worst part about it is that it will only fail in
run-time, e.g. when resolving DNS for establishing a TCP connection. I am sure
there will be some other issues too.
Author Roman Kuznetsov
License Roman Kuznetsov