Referencing derivative result (package) location when configuring NixOS using Flakes

Hello, I packaged my Hugo (static site generator) built blog (github repo) using Flakes.
Here’s the flake:

{
  description = "Nix Flake package for my blog";
  inputs.nixpkgs.url = "nixpkgs/nixos-23.11";
  outputs = { self, nixpkgs }:
    let
      pkgName = "matejasblog";
      supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
      forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
      nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
    in
    {
      packages = forAllSystems (system:
        let
          pkgs = nixpkgsFor.${system};
          drv = pkgs.stdenv.mkDerivation {
            name = pkgName;
            src = ./.;
            buildInputs = [ pkgs.hugo ];
            buildPhase = ''
              hugo
            '';
            installPhase = ''
              mkdir -p $out/var/www
              cp -r public $out/var/www/matejamaric.com
            '';
          };
        in {
          ${pkgName} = drv;
          default = drv;
        }
      );
      devShells = forAllSystems (system:
        let
          pkgs = nixpkgsFor.${system};
        in {
          default = pkgs.mkShell {
            buildInputs = with pkgs; [ git rsync hugo ];
          };
        }
      );
    };
}

How can I reference the result of my package after installing it on my system using Flakes?
Here’s an example that you can build with nixos-rebuild build-vm --flake .#server:

{
  description = "Nix Flake Nginx Example";
  inputs = {
    nixpkgs.url = "github:/NixOS/nixpkgs/nixos-23.11";
    matejasblog.url = "github:/MatejaMaric/blog";
    matejasblog.inputs.nixpkgs.follows = "nixpkgs";
  };
  outputs = {nixpkgs, matejasblog, ...}:
  let
    system = "x86_64-linux";
    nixpkgsConfig = {
      inherit system;
      overlays = [ (final: prev: { inherit matejasblog; }) ];
    };
    serverModule = { config, pkgs, ... }: {
      system.stateVersion = "23.11";
      networking.hostName = "server";
      networking.firewall.enable = false;
      time.timeZone = "Europe/Frankfurt";
      i18n.defaultLocale = "en_US.UTF-8";
      nix.settings.experimental-features = ["nix-command" "flakes"];
      virtualisation.vmVariant.virtualisation = {
        graphics = false;
        forwardPorts = [ { from = "host"; host.port = 8080; guest.port = 80; } ];
      };
      users.users.test = {
        isNormalUser = true;
        description = "Test";
        extraGroups = [ "wheel" ];
        initialPassword = "test";
      };
      environment.systemPackages = [ pkgs.matejasblog ];
      services.nginx.enable = true;
      services.nginx.virtualHosts."localhost" = {
        root = "${pkgs.matejasblog}/var/www/matejamaric.com";
      };
    };
  in {
    nixosConfigurations.server = nixpkgs.lib.nixosSystem {
      inherit system;
      pkgs = import nixpkgs nixpkgsConfig;
      modules = [ serverModule ];
    };
  };
}

If you run it and check the resulting nginx.conf you will notice that ${pkgs.matejasblog} at line 35 (root = "${pkgs.matejasblog}/var/www/matejamaric.com";)
actually resolves to the source of the package instead of it’s result (generated blog content that has an index.html).
That in turns makes the Nginx return 404 when you go to localhost:8080 in your web browser.

I’m pretty new to Nix so all answers and explanations are more than welcome! :smiley:

I would expose your package as an overlay, example here.

Then to “consume” it, you would want:

    nixpkgsConfig = {
      inherit system;
      overlays = [ matejasblog.overlays.default];
    };

Packages which get applied via overlay should then be available in the pkgs fixed point. (e.g. pkgs.matejasblog)

The issue you were likely seeing is that the flake expose a package per system, so your pkgs.matejasblog was really pkgs.matejasblog.x86_64-linux. In that regard, [ (final: prev: { inherit matejasblog.${prev.stdenv.hostPlatform.system}; }) ] would have also worked.

1 Like

Thanks a lot @jonringer! :smiley:

In that regard, [ (final: prev: { inherit matejasblog.${prev.stdenv.hostPlatform.system}; }) ] would have also worked.

For people that are maybe interested, this example didn’t quite work, but this does:

overlays = [ (final: prev: {
  matejasblog = matejasblog.packages.${prev.stdenv.hostPlatform.system}.matejasblog;
}) ];

The overlays = [ matejasblog.overlays.default]; example is definitely a lot nicer looking.
So, if somebody’s interested here’s how I modified my blog flake to work with it:

{
  description = "Nix Flake package for my blog";
  inputs.nixpkgs.url = "nixpkgs/nixos-23.11";
  outputs = { self, nixpkgs }:
    let
      pkgName = "matejasblog";
      supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
      forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
      nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
      drv = {stdenv, hugo, ...}: stdenv.mkDerivation {
        name = pkgName;
        src = ./.;
        buildInputs = [ hugo ];
        buildPhase = ''
          hugo
        '';
        installPhase = ''
          mkdir -p $out/var/www
          cp -r public $out/var/www/matejamaric.com
        '';
      };
    in
    {
      overlays.default = (final: prev: {
        matejasblog = prev.callPackage drv {};
      });
      packages = forAllSystems (system:
        let
          pkgs = nixpkgsFor.${system};
        in {
          ${pkgName} = drv pkgs;
          default = drv pkgs;
        }
      );
      devShells = forAllSystems (system:
        let
          pkgs = nixpkgsFor.${system};
        in {
          default = pkgs.mkShell {
            buildInputs = with pkgs; [ git rsync hugo ];
          };
        }
      );
    };
}
1 Like

Glad it worked out :slight_smile:

1 Like