Synchronizing inputs across Nix flakes — Hugo Sum

Synchronizing inputs across Nix flakes

I have a few projects using nix-direnv to load development environments automatically, and each of them is using a slightly different version of nixpkgs, depending on when I created them. As my laptop with 256 GB storage run out of space again, and I do not depend on any unstable software with my projects, I want to synchronize their inputs to avoid duplications and reduce storage usage.

Synchronizing inputs with nix registry

As I understand it, nix registry is just an alias to an actual input. By using an alias, you can then force multiple flakes to use the exact same version of an input.

You can define your own nix registry specific to your machine with nix.registry.

To create a registry that is called foo, and points to nixos-23.05 branch in NixOS/nixpkgs repository on GitHub, you can update your NixOS configuration as the following.

# in your configuration.nix
{ config, pkgs, lib, ... }:

{
    nix.registry = {
        foo.to = {
            owner = "NixOS";
            ref = "nixos-23.05";
            repo = "nixpkgs";
            type = "github";
        }
    };
}

And then you can use this foo as input in Nix flake. You do not need to define this variable in inputs, as missing inputs will be resolved by the registry automatically.

# flake.nix
{
  outputs = { self, foo }:
    {

    };
}

Using nix.registry can only create nix registry on your local machine. If you want to create a registry definition that can be shared among multiple machines, you can create a JSON file according to the format accepted by nix registry and host it somewhere, and point to that JSON file in the nixConfig.flake-registry in your flake.nix. A good start will be forking the global flake registry.

# flake.nix
{
  outputs = { self, foo }:
    {

    };
  nixConfig = {
    flake-registry = "https://example.com/myrepository.json";
  };
}

In my opinion you should always have nixConfig.flake-registry defined if you are using nix registry. Without defining nixConfig.flake-registry, nix registry will become a global mutable state. If you cannot control the nix registry definition in your flake consumers’ machine, you cannot guarantee your flake would build there. What if foo points to nixos-23.05 on your machine, but foo points to nixos-14.05 on their machine? What if you have two projects using the same registry name in their flake, but that name should resolve to two entirely different inputs?

Synchronizing inputs with proxy flake

Another option to synchronize inputs is to create a proxy flake. It will be used as an input by your downstream flakes and its inputs will be followed, exposing them to downstream flakes. This approach obviously works with multiple machines.

For example, I have a flake that has the following inputs as proxy.

# flake.nix
{
  description = "proxy flake for controlling input for all my flakes";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
    flake-parts = {
      url = "github:hercules-ci/flake-parts";
      inputs.nixpkgs-lib.follows = "nixpkgs";
    };

    nixos-hardware.url = "github:NixOS/nixos-hardware/master";

    nur.url = "github:nix-community/NUR/master";
    nur.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = { self, nixpkgs }:
    {

    };
}

And then I can use its inputs indirectly in my projects’ flake.

# projects/flake.nix
{
  inputs = {
    proxy-flake.url = "github:winston0410/proxy-flake?main";
    nixpkgs.follows = "proxy-flake/nixpkgs";
    flake-parts.follows = "proxy-flake/flake-parts";
  };

  outputs = { self, nixpkgs, flake-parts }:
    {

    };
}

Personally I prefer using proxy flake over nix registry, as it completely remove the possible pitfall of a global mutable state.

Upgrading inputs

To update inputs, after changing their target commit or branch in your registry definition or proxy flake, you still have to run nix flake update manually to update downstream flakes. Even though it seems to be a hassle, inputs of your projects would not update automatically without you knowing. But of course you can write a script to automate it.

No matter which approach you have chosen, you can still find sha256 hash in flake.lock to confirm which commit or branch does it resolve to.

Hugo Sum

A Hongkonger living in the UK. The only thing I know is there is so much I don't know.

Archive