A few flake-related questions

hello everyone! noob here… :nerd_face:

so, about -> follows 'nixpkgs'

what should one use that for, if a flake works just fine without it? is it for compilation tools or something? because if every flake uses its own built-in, independent compilation tools, does that mean that with PKG.inputs.nixpkgs.follows = "nixpkgs" one can use system packages and compilation tools instead? okay, then, how do i even know that it does, indeed, do exactly that? i.e. how do i see if it builds / compiles using my own compilers from my systemPackages / nixpkgs? or did i get that completely wrong and am a fool? e.g.:

inputs = {
  # ...
  yazi.url = "github:sxyazi/yazi"; # necessary for installation
  # yazi.inputs.nixpkgs.follows = "nixpkgs"; # not necessary?!
};

sometimes, the latter line could lead to compilation errors and to hash mismatches in fixed-output derivations. but why?

how does one know when is it possible to use a PKG.inputs.nixpkgs.follows = "nixpkgs"? do you actually have to look at each and every flake to see if it supports it?

okay, here is another one: i just want A package from a flake which is NOT in the main nixpkgs repo (is that how you say it?). a singular package. that’s it. is this the bare minimum for unstable?

flake.nix

{
  description = "help me"; # is *description* even required?
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    yazi.url = "github:sxyazi/yazi"; # file manager 
  };
  outputs = {
    self, 
    nixpkgs,
    # yazi, # do you need this output? why?
    ...
  } @inputs: # sorry what? WHAT IS THIS?!?!
  # sometimes they do even THIS:
  # @inputs: let inherit (self) outputs; in # WTF?!
  { nixosConfigurations.HOST = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux"; # LEGACY?! do you still need this? 
      specialArgs = {
        inherit inputs; # why? why? whyyyy?!
        # or
        # inherit inputs outputs PKG; # please help me
        # or
        # inherit inputs nixpkgs; # i am utterly lost
      };
      # ... 
      # sometimes people have more stuff HERE
      # ...
      modules = [
        ./configuration.nix # understandable
        # ./hardware-configuration.nix # NOT understandable!!
        # yazi.nixosModules.default # unnecessary? why would you want this?
      ];
    };
  };
  # ...
  # again, some people have EVEN MORE stuff down here! 
  # ...
}

configuration.nix

{
  config, # config = { }
  lib, # mkDefault mkForce
  pkgs,
  inputs, # flakes
  ...
}:

{
# ...
  environment.systemPackages = {
    inputs.yazi.packages.${pkgs.system}.default;
    # okay, SLOW DOWN. what is all of this:
    # ${pkgs.system}?
    # is it the same as
    # ${pkgs.stdenv.hostPlatform.system}?
    # next, why doesnt it work without *inputs.*?!
    # in some guides they just tell you to
    # "place the pkgs.PKG in *systemPackages*"
    # but it never worked!
  };
# ...
}

lastly, this question bothers me the most. could be a little silly, but i do need to ask this:

for example, i am NOT using nixos-unstable, rather, i am using nixos-unstable-small. do you have to accommodate for the prefix or not? for example:

inputs = {
  # nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
  nixpkgs-small.url = "github:nixos/nixpkgs/nixos-unstable-small";
  yazi.url = "github:sxyazi/yazi";
  yazi.inputs.nixpkgs.follows = "nixpkgs-small"; # is that right?
};

i just really want to understand this before i go ahead and try home-manager. which has like 3 different ways of installation inside a flake.nix: the external .nix file configuration, the nixosModules configuration - the one that is strangely without the equals mark before { }, and a separate homeConfigurations."USER@HOST" = home-manager.lib.homeManagerConfiguration - all of them are just… infinitely interesting.

sorry, it surely is quite a bit silly, but, yeah… thanks in advance!!! :sweat_smile:

1 Like

Not system dependencies, but the same nixpkgs as the one specified in your flake (or your flake registry, if you don’t define nixpkgs in your flake).

This is important, because without it, every single flake you import will have its own copy of nixpkgs, meaning that every flake you use roughly doubles your system closure size. You also have no control over when your recursive inputs update if you don’t set it, meaning you have to rely on all upstreams adequately updating.

I find it strange that it would lead to that in fixed output derivations, the fetchers would need to change for that, which is very uncommon. Maybe you’re making it use very outdated versions of nixpkgs?

However, errors when you do that are quite expected. It’s not unusual for dependency versions to change, becoming incompatible with the flake’s actual source; you’re substituting a different set of versions after all, there is no guarantee that this will work.

Pretty much. You also have to check their recursive dependencies. You check if their inputs contain a nixpkgs; if they do, you can change their nixpkgs to yours with .follows. You can do this for all other inputs, too (though luckily it’s usually less essential than for nixpkgs).

To see if it will be successful is trial-and-error. In addition, some flakes depend on nixpkgs multiple times, so you even have to check if your substitution actually deduplicates as you intend it to.

Yep, you do have to do that.

All of this is one of the glaring design flaws of flakes which haven’t seen much attention in the last 5 years (besides subbing in a proprietary web service, promising to eventually work with the community to release a proper fix to the schema, and then just leaving the flakes concept to rot).

My personal suggestion is to just ignore flakes for now, at least for NixOS systems - you barely have a use case for them, npins can do all of the input management just fine. But I suppose you’ve already started going down the route.

Ignoring .follows is fine, but it has a lot of downsides. Specifying it is fine, too, and has fewer risks, but involves a lot of drudgework, understanding of the dependency tree, and trial-and-error. Flake inputs are unfortunately kind of inherently broken.

3 Likes

follows shrinks your lockfile size, nothing more. It increases the chances of incompatibility and you have to manage that as it arises.

Also you probably don’t want to use -small, it’s going to have less packages cached.

6 Likes

oh wowie, thank you guys, A LOT!! i will certainly try npins, im completely fine with trying new stuff. but yeah, i definitely wasnt expecting such a hasty reply, i was in the middle of editing and adding another question, which is about… the minimal requirements to install A flake package (see above). could you please give some light on that? :innocent:

and yeah, i know, less cache, but i am literally only using TUI stuff. im that kind of person… lolz :stuck_out_tongue:

by the way… speaking of unstable-small… who dictates which kind of packages get the cache?

You’ll have to expand on what you mean by that. I suppose the “minimal requirement” is a nix version that supports flakes, which is practically all of them now?

You’d be surprised, you can easily eat another 30G of space just from bonus stdlibs if you have a sufficiently deep flake tree. Not to mention the cost in evaluation time and memory use; each separate nixpkgs instance takes about 250MB, so with just 10 you’re nearing 3GB of memory use every time you evaluate your config (assuming each instance is actually used).

And again, you’re not getting updates at the cadence you expect when you run nix flake update, which is a pretty big deal.

It’s defined here: nixpkgs/nixos/release-small.nix at master · NixOS/nixpkgs · GitHub

The answer to who is responsible, well, the nixpkgs contributors. You can send PRs in yourself, if you have a reasonable argument for why something should be in there.

It’s not intended for desktop use, though, so since you’re using home-manager I agree with @waffle8946 here, you shouldn’t be using a *-small channel, it just forces you to build lots of software locally that you don’t need to build; sometimes waiting an extra day for an update isn’t going to cause issues for you.

2 Likes

sorry, i have edited my original post, the question is there, in the middle. uhm, about compiling. okay, then i shall switch back for now. i will be soon getting a ridiculous 128 core ARM monster that i will be using as a uhm, a substituter? you know, the remote builder. what do you call them…

p.s. i’ve heard that i could contribute to the binary cache by providing my own computational power - they probably phrased it better than me, but i wonder how easy it is to actually do that? i bet you need to go through lots of security checks first… i probably will be using these 128 cores for my personal use, but… im just wondering if i can help anyhow

Ah, I see, lemme reply to your comments.

You can keep it empty if you like. It’s nice for documentation purposes.

That’s not an output, it’s your input. It passes your input’s actual value into your output function; without it you can’t actually use the yazi input.

That said…

It’s nix syntax for “take all the stuff from the destructured attrset being used as a function argument that precedes this and also put it in this variable”.

It basically lets you also say inputs.yazi and inputs.nixpkgs, instead of using the nixpkgs and yazi bindings you defined previously.

This seems niche, but especially for flakes it’s super useful, because:

Right, so specialArgs lets you inject things into the modules you define. That’s jargon - you know how in your configuration.nix it starts something like this?

{ pkgs, lib, ... }: {

}

Ever wondered where the pkgs and lib come from? Well, they’re defined by the nixpkgs modules, and you can use specialArgs to add your own.

So when you do:

specialArgs = { inherit inputs; };

# btw that's just short for:
# specialArgs.inputs = inputs;

You can now do:

{ pkgs, lib, inputs, ... }: {

}

And this is super useful, because now all your flake inputs are accessible from your configuration.nix through inputs, which means you don’t have to do any of the hacky stuff that lots of people do in their flake.nix (some of which you seem exasperated by) because they don’t understand NixOS.

Hacky stuff like this

Yeah, that’s stupid. outputs is available through self if you inherit inputs, nixpkgs is available through inputs.nixpkgs. PKG is likely retrieved using a inputs.nixpkgs.legacyPackages......, which you could just do in configuration.nix.

Don’t learn from people who do stuff like that.

I’m aware! For NixOS systems, you rarely ever need more stuff. Just ignore it unless you know how it works.

Someone wanted self to be renamed to outputs and wrote that code in the most confusing way possible. Or they hve no idea what they’re doing and were throwing shit at the wall until it evaluated. Don’t copy that.

Yeah, if you just want a single package that’s unnecessary. If you want to use the module defined in the flake, you need it. I don’t use yazi, but lots of software packaged for NixOS needs to be activated with a module, so perhaps you need that.

Either way, you shouldn’t put it in your flake.nix. Instead, put it in your configuration.nix, using the specialArgs trick as explained earlier:

# configuration.nix
{ inputs, ... }: {
  imports = [
    inputs.yazi.nixosModules.default
  ];
}

or, if you really just need a package:

# configuration.nix
{ pkgs, inputs, ... }: {
  environment.systemPackages = [
    inputs.yazi.packages.${pkgs.system}.default # Or .yazi, depends on what they define
  ];
}
3 Likes

Oh, more questions:

Yep. The point is, flake outputs require specifying the platform of the package you want. This is because cross-platform builds are first class citizens in the flake world. So you need to know the system triple for the system you’re deploying to be able to pick the correct package. That does that.

Well, as I explained in my previous post, you’ve passed inputs into specialArgs. So the only handle you have for the yazi flake packages is inputs.yazi.

You can also define an “overlay”, which injects all the yazi packages into your nixpkgs’ pkgs. This used to be how people depended on third party package repositories with NixOS, but nowadays it’s basically just done because people haven’t realized specialArgs exists yet. It’s a bad idea to use overlays and flakes together most of the time, unless you actually want to override a package in nixpkgs. You can mostly ignore those guides.

2 Likes

oh that is extremely useful! well done! :clap:

yeah… i always had these INTERESTING lines commented out… i always wanted to ask what are they even for… now i know. thanks a lot!!

very interesting indeed… but

what about specialArgs = attrs;? is this for channels?

also, one more final question. perhaps a very difficult one. so… how do i even put this. getting A package from a flake that is literally just A package (singular) is easy. but what if… what if the flake has A LOT OF THEM? such as:

chaotic.url = "github:chaotic-cx/nyx/nyxpkgs-unstable"; # what
jovian-nixos.url = "github:jovian-experiments/jovian-nixos"; # the
nur.url = "github:nix-community/NUR"; # NUR?!

and i only want just that SPECIFIC one? e.g. a pkgs.steamdeck-firmware from jovian-nixos flake. they basically tell you to install the whole thing. NO, i want just “that” package. but how?!

is it possible to NOT compile every single stuff in the flake and instead choose only those that one would want? or do i completely misunderstand flakes? what happens when you add ONLY the input? does it begin to compile only after i give it an inputs.PKG.packages.${pkgs.system}.default; - is that right?

ahh… im just a question making machine at this point. but there is just so much stuff, honestly. i mean, SOME flakes come with sha256 hashes that you have to somehow get. i’ve heard one way to do this is via sha256 = lib.fakeHash; - to let it fail and reveal an actual proper hash.

AND THEN, it just never ends. people use stuff like builtins.fetchTarball to download a tar archive. BUT WHY?! that doesnt look right. is that even technically a flake? am i wrong? why do other flakes work without this?

also… about CUSTOM flakes. quite literally. so to make a FLAKE.url = "github:USER/THING"; actually work you will need a flake.nix in the repo, correct? what if there is none of that? for example, i want THIS - durdraw, which is not in the repo. i could A) issue a PR asking for this to be added to nixpkgs or B) …what else? use fetch*? how can i make this work with flakes? or can i literally just compile that myself? that wouldn’t be very nix-way-ish, right?

that is a lot of question marks! :crazy_face:

1 Like

sorry, i could not resist just a few more flake-related questions… i still had lots of spare question marks lying around :stuck_out_tongue: (see above)

Nit: this is not exactly true. In the flake world, you’re merely forced to make the system string explicit. This has nothing to do with cross compilation which is a downstream concept that lives entirely in Nixpkgs. Nix is not aware of that, all it knows is which system a derivation‘s builder executable will run on, which can be used e.g. to dispatch remote builds.

Other than that, thanks a lot @TLATER @waffle8946 for the detailed replies! @frozen.frog23 consider to take it more slowly with Nix and first go through the most important tutorials that should help get an idea for what’s even going on:

5 Likes

oh, okay! i mean i am taking it slowly, i’ve started in… january this year and look how far i’ve gotten, incredibly! :crazy_face:

no need for a vm when the system’s this stable, haha :smiley: :+1:

:heart:

3 Likes

@fricklerhandwerk is right that you probably should slow down a lil’ and look at the docs; but since you have more specific questions and it’s right before bed, let me just quickly knock those out:

Yep. Other packages will usually be available in the same attribute, actually, so you can just:

# configuration.nix
{ pkgs, inputs, ... }: {
  environment.systemPackages = let
    chaotic = inputs.chaotic.packages.${pkgs.system};
  in [
    # I have no idea what packages would be in that flake
    chaotic.firefox
    chaotic.alacritty
    chaotic.etc
  ];
}

NUR is more complex because it’s a historic thing, you will likely need to use overlays for it. But I find myself never reaching for the NUR nowadays.

You misunderstand; that’s not “flakes”. See the docs on packages, there’s a huge difference between a fixed-input derivation and a flake.

Erm, it’s not “a flake”. It’s just a fixed-output derivation. Such derivations could contain nix code, and that nix code may be a flake.

You should not be importing nix code from derivations like that in either case, use flakes or npins instead.

It’s mostly used in guides that insist on having things be short and self-contained at the cost of being more confusing and pushing footguns into people’s hands. The better ones - like what @fricklerhandwerk links - at least eventually also tell you how to do things properly.

That’s correct. The flake syntax lets you define flake = false to put non-flake stuff in your inputs. You can then manually import the - usually - default.nix:

# configuration.nix
{ inputs, ... }: let
  some-custom-thing = import inputs.some-custom-thing { };
in {
  environment.systemPackages = [
    some-custom-thing
  ];
}

The big difference is that flakes define a default set of outputs that you can rely on; i.e., you know that .packages contains the packages included in that flake.

If you use a non-flake dependency, you’re in the wild west, and need to figure out how to use the repository on your own. It might be that default.nix contains a package and that the above works. It could also contain a nested set of packages and you might need to do more stuff. It might not even have a default.nix in the root directory and the above fails miserably.

This is one of the two~ish problems that flakes set out to solve, so unfortunately, there is no real alternative, besides reading repository documentation and understanding nix.

2 Likes