Nix and GHC package database management: automatic vs custom database
0 reputation · 07 Feb 2020, 14:31 UTC
0 reputation · 07 Feb 2020, 14:31 UTC
The goal is to decide whether to allow Nix to automatically populate the GHC package database from the Nix store or to override it with a custom database location for incremental Haskell builds.
Nix creates pure build environments that isolate GHC and its dependencies, placing the package database in the Nix store rather than the usual Cabal or Stack user database. This separation means that using Cabal or Stack commands outside the Nix shell can lead to mismatched package databases and potential build failures, and the exact behavior may shift between different Nixpkgs releases.
Community discussions show mixed approaches: some projects accept Nix’s default management, while others explicitly set GHC_PACKAGE_PATH to maintain control over package registration, raising questions about how each choice affects incremental build cache validity and long‑term reproducibility.
What are the trade‑offs of using Nix’s default package database versus a custom database managed via GHC_PACKAGE_PATH? How does each approach influence incremental build cache validity and reproducibility across Nixpkgs versions?
34775 reputation · 07 Feb 2020, 19:15 UTC
Use Nix’s default, read‑only package database when reproducibility across machines and Nixpkgs versions is the priority. Override it with a custom database via GHC_PACKAGE_PATH only if you need faster incremental builds and can accept the responsibility of keeping the database in sync with the Nix expression.
The default Nix approach stores the GHC package database inside the Nix store, which is immutable and uniquely identified by the hash of all inputs. This guarantees that every developer and CI job sees exactly the same set of package IDs, so a build that succeeds locally will also succeed remotely, regardless of later Nixpkgs updates (as long as the same derivation is used). The downside is that any change to a Haskell source file forces Nix to treat the derivation as changed, often triggering a rebuild of the whole closure and slowing inner‑loop development.
When you point GHC_PACKAGE_PATH to a mutable directory, GHC can register packages there without re‑linking against the Nix store. This lets tools like cabal build or stack build reuse previously compiled objects, giving a noticeable speed‑up for small edits. However, the database now lives outside Nix’s purity guarantees: it can accumulate packages that were not declared in the .nix, drift from the store‑provided versions, and become invalid if the underlying GHC or core library paths change in a new Nixpkgs revision.
GHC_PACKAGE_PATH enable mutable package registration, which can speed up incremental builds by avoiding constant re‑linking.GHC_PACKAGE_PATH introduces impurity; the local build state may diverge from the Nix expression, breaking reproducibility.nix-shell or use nix develop. No extra configuration is needed; GHC will automatically use the store‑located package database.$HOME/.ghc-custom-db.ghc-pkg init $HOME/.ghc-custom-db.export GHC_PACKAGE_PATH=$HOME/.ghc-custom-db (or prepend it if you want to keep the store DB as a fallback).ghc-pkg register /nix/store/...-package.conf for each dependency, or let Cabal/Stack do it via cabal v2-build or stack build after setting the variable.Are you primarily using cabal (with v2-build or new-build) or stack for your Haskell builds? The answer affects whether GHC_PACKAGE_PATH is honoured and how you should register packages, which could change the recommendation.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 07 Feb 2020, 22:36 UTC
One clarification worth adding: Nix's "automatic" behavior isn't just a default path — ghcWithPackages produces a wrapper script that sets GHC_PACKAGE_PATH for you. So if you export your own value in a shell hook, you may be replacing the wrapper's setting rather than layering on top of it, and the store database silently drops out of scope. That's a common source of the "package database mismatch" errors mentioned above.
A quick way to verify what GHC actually sees, regardless of which approach you pick:
ghc-pkg list
ghc --print-global-package-dbThe first shows every database in scope and its registered packages; the second confirms the global database GHC falls back to. If your custom path appears but the store path doesn't, incremental builds may link against stale registrations after a Nixpkgs bump. Behavior here has shifted across Nixpkgs releases, so treat the exact layout as version-dependent and re-check after upgrades.