Adding overlays in `haskell.nix`

So I’m using flake.nix along with haskell.nix (IOHK), but I’m stumped and I cannot find any documentation on how to overlay a haskell package.
I have a flake.nix file like so:

{
  # This is a template created by `hix init`
  inputs.haskellNix.url = "github:input-output-hk/haskell.nix";
  inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable";
  inputs.flake-utils.url = "github:numtide/flake-utils";
  outputs = { self, nixpkgs, flake-utils, haskellNix }:
    let
      supportedSystems = [
        "x86_64-linux"
        # uncomment additional systems as needed
        # "x86_64-darwin"
        # "aarch64-linux"
        # "aarch64-darwin"
      ];
    in flake-utils.lib.eachSystem supportedSystems (system:
      let
        pkgsOrig = import nixpkgs {inherit system;};
        overlays = [
          (import ./overlays/haskell-overlays.nix)
          haskellNix.overlay
          (final: prev: {
            hixProject = final.haskell-nix.hix.project {
              src = ./.;
              evalSystem = system;
            };
          })
        ];
        pkgs = import nixpkgs {
          inherit system overlays;
          inherit (haskellNix) config;
        };
        flake = pkgs.hixProject.flake { };
      in flake // {
        legacyPackages = pkgs;

        packages = flake.packages // { default = flake.packages."testpkg"; };
      });

  # --- Flake Local Nix Configuration ----------------------------
  nixConfig = {
    # This sets the flake to use the IOG nix cache.
    # Nix should ask for permission before using it,
    # but remove it here if you do not want it to.
    extra-substituters = [ "https://cache.iog.io" ];
    extra-trusted-public-keys =
      [ "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" ];
    allow-import-from-derivation = "true";
  };
}

This was created by the haskell.nix template (and needed some adjustments since it used an obsolete compiler).

As you can see, I have an overlay I’ve defined in ./overlays/haskell-overlays.nix. But this overlay is not updating the package set. The package I’m trying to overlay is still the older version as per cabal’s output.

Am I not understanding something basic about applying overlays with using haskell.nix and flakes?

I’ve personally found srid/haskell-flake much easier to use and better documented than haskell.nix. here’s the documentation on overlaying a package. and package settings can be used to tweak e.g. dependencies, cabal settings, etc., on a per-package basis.

as haskell-flake uses flake-parts and the standard nixpkgs haskell infrastructure, it’s much simpler to use in practice. for example, each of the settings you can tweak on a package map directly to functions under pkgs.haskell.lib.compose, found here. all haskell-flake does is expose a nice UI for accessing the underlying nixpkgs infrastructure.

1 Like

What was in overlays/haskell-overlays.nix? What were you trying to override?

Keep in mind that haskell.nix does not use the nixpkgs pkgs.haskell (except for bootstrapping). It also does not modify pkgs.haskell at all. Instead haskell.nix will follow the build plan of the cabal.project or stack.yaml if present (if neither is present it works like cabal with no cabal.project).

Assuming there is no stack.yaml then you might want to specify the GHC version to use to build the plan.

If you want to select the GHC version used to build testpkg, then you can set it in that in the flake.nix by adding compiler-nix-name like this:

hixProject = final.haskell-nix.hix.project {
  src = ./.;
  evalSystem = system;
  compiler-nix-name = "ghc9122";
};

Or you could create and git add (modifying files already in git works, but any new files need a git add or they will be filtered out by nix flake commands) a nix/hix.nix file with extra project arguments, so just:

{
  compiler-nix-name = "ghc9122";
}

Because it uses the cabal.project file, a lot of other things you might want to override you can put in the cabal.project file. Most settings in the cabal.project file will be respected (even repositories and source-repository-packages if you add special comment --sha256 with a hash to help nix download the files).

If you create a cabal.project file, make sure you git add it.

For things that cannot be specified there you can pass other project arguments, most notably modules where you can override things in the cabal plan:

For instance say you wanted to turn on -Werror for all packages you could do this in cabal.project:

package *
  ghc-options: -Werror

However haskell.nix will not see that (due to a limitation of plan.json). So to make it work you would need:

{
  compiler-nix-name = "ghc9122";
  modules = [{
    # To set it for all packages (like `package *`)
    ghcOptions = ["-Werror"];
    # To set it for `package somepackage`
    packages.somepackage.ghcOptions = ["-Werror"];
    # To set it for single components
    packages.somepackage.components.library.ghcOptions = ["-Werror"];
    packages.somepackage.components.exes.someexe.ghcOptions = ["-Werror"];
  }];
}

One thing that does look a bit odd in the example is:

packages = flake.packages // { default = flake.packages."testpkg"; };

That default looks wrong because haskell.nix builds cabal components, not whole cabal packages. It should probable be one of these:

packages = flake.packages // { default = flake.packages."testpkg:exe:testpkg"; };
packages = flake.packages // { default = flake.packages."testpkg:lib:testpkg"; };

You can see a list of the flake.packages with nix flake show --allow-import-from-derivation. To get any component from the project (not just the “local” packages):

packages = flake.packages // { default = pkgs.hixProject.hsPkgs.somepackage.components.exes.someexe"; };
packages = flake.packages // { default = pkgs.hixProject.hsPkgs.somepackage.components.library; };

Edit: Added missing .hsPkgs

In case what you need to override cannot be specified in the cabal.project file, here are the options you can use in modules arg:

Here are the options that work at any level (global, package or component):

These only work at the component level:

This was the overlay:

final: prev:
let
  sources = import ../nix/sources.nix;

  inherit (prev.lib) composeExtensions;
  inherit (prev.lib.lists) foldl;

  haskellOverrides = finalh: prevh: {
    monad-log = finalh.callCabal2nix "monad-log" sources.monad-log {};
  };
in {
  haskellPackages = prev.haskellPackages.override (old: {
    overrides = foldl composeExtensions (old.overrides or (_: _: { }))
      [ haskellOverrides ];
  });
}

And monad-log pointed to by sources (using niv) was a latter version in a forked copy.

You don’t need to use nix code for that when using haskell.nix. Instead just git add a cabal.project file with something like:

packages: .

source-repository-package
  type: git
  location: https://github.com/winterland1989/monad-log
  tag: ebf935c84961bf108f89eb0d90ddc602e5ecf7f4
  --sha256: sha256-9LW3pdtM8kJcW3ve73vyk4so+8+ROojClLxbeZpdZus=

Every time you change the tag you will need to update the --sha256 as well (or nix will keep using the old version). To update it you can just replace some of the chars in the hash with 0s and attempt to rebuild. You will get an error like this:

error: hash mismatch in fixed-output derivation '/nix/store/46dv2pdymvbydnc9qzd228xy09z5b748-monad-log-ebf935c.drv':
         specified: sha256-9LW3pdtM8kJcW3ve73vyk4so+8+ROojClLx00ZpdZus=
            got:    sha256-9LW3pdtM8kJcW3ve73vyk4so+8+ROojClLxbeZpdZus=

Then just replace the incorrect hash with the correct one.

Other useful things you can include in cabal.project for cabal and haskell.nix to use:

-- Pin the version of hackage used
index-state: 2025-07-01T00:00:00Z

-- Restrict versions selected
constraints: somepackage ==1.0.0.0

-- Set flags
package somepackage
  flags: +someflag -someotherflag

Because haskell.nix uses the plan.json created by the cabal planner it is able to mirror the behaviour of cabal closely in all these cases (without any special nix code).

I see, but this is still not working for me. I added a cabal.project as you instructed, but it makes no difference. I’ve also corrected the flake.packages."testpkg:lib:testpkg" as well. The cabal.project file is also staged and my cabal.project file now looks like:

packages: .

source-repository-package:
        type: git
        location: https://github.com/handle/monad-log.git
        tag: 8e8c778ae1059a08d7f10e2609a1f89abcdc0469
        -- sha256: sha256-9LW3pdtM8kJcW3ve73vyk4so+8+ROojClLxbeZpdZus=

but it just seems to be ignored.

It is also terribly confusing to me (if my understanding of fixed points is correct) to understand why overlays in the traditional nix fashion don’t take effect, while the ones added in flake.nix do. IMHO, that just adds unnecessary exceptions and confusion.

And here’s monad-log.cabal.patch:

---
 monad-log.cabal | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/monad-log.cabal b/monad-log.cabal
index 9337e04..d83bf07 100644
--- a/monad-log.cabal
+++ b/monad-log.cabal
@@ -1,5 +1,5 @@
 name:                monad-log
-version:             0.1.1.0
+version:             1.0.0.0
 synopsis:            A simple and fast logging monad
 description:         
     This package provide a mtl style `MonadLog` class and a concrete monad transformer `LogT`, the main difference between this package and monad-logger are:
@@ -41,9 +41,9 @@ library
                     ,   Control.Monad.Log.LogThreadId
   -- other-modules:       
   -- other-extensions:    
-  build-depends:        base >=4.6 && <5
+  build-depends:        base
                     ,   fast-logger 
-                    ,   monad-control >=0.3 && <1.1
+                    ,   monad-control
                     ,   lifted-base 
                     ,   exceptions 
                     ,   bytestring

But when doing nix develop I still see:

... 
       > Resolving dependencies...
       > Error: [Cabal-7107]
       > Could not resolve dependencies:
       > [__0] trying: testpkg-1.1.0 (user goal)
       > [__1] trying: monad-log-0.1.1.0 (dependency of testpkg)
       > [__2] next goal: fast-logger (dependency of testpkg)
...

It needs to be source-repository-package not source-repository-package: and --sha256: not -- sha256:.

It also looks like you updated the tag without updating the --sha256: comment.

Try:

packages: .

source-repository-package
        type: git
        location: https://github.com/handle/monad-log.git
        tag: 8e8c778ae1059a08d7f10e2609a1f89abcdc0469
        --sha256: sha256-9LW3pdtM8kJcW3ve73vyk4so+8+ROojClLxbeZ00000=

However https://github.com/handle/monad-log.git will need to point to a public git repo with 8e8c778ae (I forget how to make it work for a private repo).

Once that is sorted it should give an error as described with the correct value to use for the --sha256:.

1 Like

Thank you very much, that makes sense.
However, using public repos would be a no-go in my case.

This actually worked out of the box, so thanks! I didn’t know this project existed.

1 Like

One way to use a private repo is to use a git submodule to include the monad-log repo as a subdirectory and then the cabal.project can just be:

packages:
  .
  ./monad-log

Unfortunately you have to tell nix to look for submodules in the flake url like this:

nix flake show '.?submodules=1#' --allow-import-from-derivation
nix develop '.?submodules=1#'
nix build '.?submodules=1#'

If you don’t like submodules you could just add a copy of the code into the repo (assuming it is not going to cause unwanted duplication).