Good practice for Nix Flakes

.i .ui coi rodo

I’ve been diving into nix flakes recently, and had some questions about the best practices of building my things using them.

I’ve been attempting to (a lot of the time to no prevail), package programs as a flake, containing a single package, and an overlay containing that package.

As an example:

{
  description = "example flake";
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-20.09";
    flake-utils.url = "github:numtide/flake-utils";
  };
  outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: 
  let pkgs = nixpkgs.legacyPackages.${system};
  in {
    overlay = final: prev: {
      vim = self.defaultPackage;
    };
    defaultPackage = pkgs.vim_configurable.customize {
      name = "vim";
      vimrcConfig.customRC = ''
        set colorcolumn=80
      '';
    };
  });
}

(side note, this exact code hasnt been working for me, related to the overlay, but for the purposes of the question hopefully it’ll convey the gist of what Im trying to do)

from what I understand, this allows users to use the new nix shell to grab this, nix shell github:blah/blah, and users who are using flakes for their system configuration can use input-name.overlay to add the override to their package set.

Is this how I should be going about using flakes? I’ve had trouble figuring out the best practices from what Ive read, about what outputs I should have, and whether or not to use flake-utils (if I don’t, I’m not sure how to choose what systems to allow, or whether its too narrow to just use the one I’d be using).

I guess the main things I’m asking are:

  • What’s a good skeleton for packaging a program as a flake
  • What’s good practice for defining systems
  • What outputs are required / good to define / unhelpful, when packaging a program

Any help would be appreciated a lot, thank you :slight_smile:

2 Likes

One way of doing it is to inverse what you have been trying to do.

First, declare an overlay that includes your packages. Do this like you would have done in a normal Nix setting:
overlay.nix:

self: super:
{
  my_prefix = {
    vim = super.vim_configurable.customize {
      name = "vim";
      vimrcConfig.customRC = ''
        set colorcolumn=80
      '';
    };
    devShell = super.mkShell {
      buildInputs = [ super.jq ];
    };
  };
}

Then create your own instance of nixpkgs in the flake that includes that overlay:
flake.nix:

{
  description = "example flake";
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-20.09";
    flake-utils.url = "github:numtide/flake-utils";
  };
  outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: 
  let pkgs = import nixpkgs { overlays = [ (import ./overlay.nix) ]; };
  in {
    defaultPackage = pkgs.my_prefix.vim;
    devShell = pkgs.devShell;
  });
}

Hope that gives you a good start!

4 Likes

Thank you for the help :slight_smile:

I have two questions about it if thats ok.
Firstly, what does the super.jq build input do for the devShell? from my understanding, super will refer to the pre-overlay package set, but im unsure why the jq package specifically is needed here?

Secondly, what would be the preferred way of including this package in a flakes based NixOS system configuration? my original plan was to expose an overlay, and then apply the overlay of any flakes that I was using as an input into my system, to create one large package set with all overlays applied to it.

Is there a better way to include these packages, or should I also populate the outputs.overlay with the contents of overlay.nix?

Thank you

That’s a fair question. jq is meant to be an example and you can replace it with any other package that you want to have available when entering the nix develop environment.

Typically you use super to access packages, unless they are on the same overlay, in that case, you have to use self.

NixOS creates its own instance of nixpkgs. I don’t know that it’s possible to pass an existing instance. That being said, you can set the same overlay to the nixpkgs.overlays NixOS options. Here is an example of how to do this:

{
  description = "example flake";
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-20.09";
    flake-utils.url = "github:numtide/flake-utils";
  };
  outputs = { self, nixpkgs, flake-utils, ... }: 
    {
       nixosConfigurations.my-host = { ... }: {
         # Here, put the NixOS configuration for your host.
         # ...
         # And set the same overlay in the nixpkgs configuration:
         nixpkgs.overlays = [ (import ./overlay.nix) ];  
       };
    } // (
     flake-utils.lib.eachDefaultSystem (system: 
       let pkgs = import nixpkgs { overlays = [ (import ./overlay.nix) ]; };
       in {
         defaultPackage = pkgs.my_prefix.vim;
         devShell = pkgs.devShell;
      });
   };
}

The overlay can also be stored in a local let binding instead of a separate file if you prefer it like that.