I E

Per-project Nix substituters

10 August, 2024

A lot of Nix projects end up with their own binary cache (also known as a “substituter”). Nix can be quite slow when it’s configured to use many project-specific substituters1, because it queries them all when checking if it needs to build a derivation. In a specific project, most of the other substituters Nix knows about are irrelevant, and querying them is wasted effort. My solution is to only enable https://cache.nixos.org globally, and to selectively enable other substituters while I’m working on a project that needs them. Here’s how I do it.

Setup

In configuration.nix, set the system’s trusted substituters2:

nix.settings.trusted-substituters = [
  "https://cache.nixos.org/"
  "substituter-1"
  "substituter-2"
  "..."
  "substituter-N"
];

nix.settings.trusted-public-keys = [
  "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
  "other-public-key-1"
  "other-public-key-2"
  "..."
  "other-public-key-N"
];

And set Nix’s default substituter:

nix.settings.substituters = [
  "https://cache.nixos.org/"
];

By default, only the substituters in substituters will be used by Nix. trusted-substituters lists the substituters that regular users are allowed to use in Nix builds.

Without Flakes

To enable other substituters, use the NIX_CONF environment variable with the extra-substituters3,4 setting:

export NIX_CONF="extra-substituters = substituter-42"

Or use substituters to override the system’s substituters:

export NIX_CONF="substituters = substituter-42"

If the substituters named in these local settings aren’t defined in the trusted-substituters system setting, then Nix will ignore them and print the following message:

warning: ignoring untrusted substituter '{substituter URL}', you are not a trusted user.

I use direnv to automatically set and unset NIX_CONF by putting the export NIX_CONF=... command inside a .envrc in the project root.

With Flakes

The nixConfig attribute can be used to set Nix configuration from inside a Flake5:

{
  nixConfig.extra-substituters = [
    "substituter-42"
  ];
  
  inputs = ...;
  
  outputs = ...;
}

By default, Nix will ask for permission to use the config specified by the Flake. I use nix-direnv to automatically load and unload Nix develop shells when switching projects, and the permission prompt breaks direnv on my shell.

To work around this, I enable the accept-flake-config Nix option in my configuration.nix:

nix.extraOptions = ''
  experimental-features = nix-command flakes
  accept-flake-config = true
'';

  1. Some relevant discussions:

    ↩︎
  2. trusted-substitutersnix.conf reference↩︎

  3. A configuration setting usually overrides any previous value. However, for settings that take a list of items, you can prefix the name of the setting by extra- to append to the previous value.

    nix.conf reference

    ↩︎
  4. substitutersnix.conf reference↩︎

  5. See nixConfig in Flake format — Nix Flake reference↩︎