Starting using flakes for system, projects and home-manager


I read the blogs from @edolstra, and @zimbatm to understand a bit more what flakes could give me, and I started to convert my config using them.

My previous setup was:

  • a project containing a custom pkg set, nixos modules and home-manager modules to help me
  • three channels: nixos-20.03, nixpkgs-unstable and home-manager to find the different sources I need

I started adding a flake in my project, and presenting the overlay in this news way.

Current result:

  • My flake exports my modules, my packages + some packages from unstable and the home-manager from their master
  • My nixos system is now using the flake variant
  • My projects are easy to convert from shell.nix to flake.nix with devShell it is a bit more verbose but easier to share with other people.
  • home-manager … well … still a pain point.

Home-manager’s master branch has a flake.nix file, but the code still looks for packages from channels and user’s overlay, and it requires a config path to find its modules, for the moment I had to hardcode the /nix/store path as it doesn’t use my flake’s context to resolve the “pkgs.home-manager” value

Has anyone succeeded to tell home-manager to use a flake’s context ?
Something like an import function taking a flake and using the outputs would be great.

Thank you !

PS: I guess nix-darwin will also need such a solution

Hijacking a little but even after reading the posts about Flakes; I struggle to find the value proposition.

it seems like it has some special handling for GitHub repositories to “add flakes”; but what’s the benefit over a raw default.nix in a repository ?

I see it as an interface for packages, modules, … and pinned “channels”. It is a bit clearer what comes from the original channel, and what is part of the local overlay.

That said, there is no way to propagate the channels I think (that’s my current problem)

But that’s only how I understood it

I also didn’t really see the value in them, until I transferred my own configuration + packages to flakes. I really like that it has a universal interface: with flakes it is really obvious what a repository has to offer. Does the flake have a packages key? Then it supplies at least a packaged application. Does it have a nixosModules key? Then it provides module(s) for nixos. Et cetera. And you can query this information with a simple nix flake show.

In contrast, for default.nix there was exactly 1 rule about its contents: it must be a nix expression. But it could be a function, or a derivation, or a set of derivations, anything basically. So you would always have to read the code to understand what’s going on.


I think Eelco once said in a talk that Nix is not good at packaging Nix files, which is ironical for a package manager.

You could see flakes as a standard way of packaging Nix files. It has a predefined set of attributes, including predefined output attributes (which can be verified with nix check). This has the large benefit of providing a uniform interface to Nix repositories (along with discoverability).

Another large benefits of flakes is they fully specify their inputs, wherein the exact revisions/hashes of a repository are pinned in a lock file. Together with disallowing impure inputs, providing hermetic evaluation. This gives full reproducibility and proper caching.

1 Like

That said, there is no way to propagate the channels I think (that’s my current problem)

I don’t know if I understand you correctly, but you can propagate inputs to your dependencies like this:

inputs.home-manager = {
  url = "github:rycee/home-manager/master";
  inputs.nixpkgs.follows = "nixpkgs";

Then you can import Home Manager’s NixOS module in the modules section you pass to nixosSystem:

outputs = { self, nixpkgs, home-manager }: {
  nixosConfigurations.myConfig = nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    modules = [
      ({ config, lib, pkgs, ... }: {
        # Your NixOS configuration...
        home-manager.users.freezeboy = {
          # Your Home Manager configuration...

Maybe you have to set home-manager.useGlobalPkgs = true; to make Home Manager use the correct pkgs? I have it in my config at least, and with the setup as described above I have no issues, and I do not have to hardcode the path to the nix store.


I am not sure if this is cheating, but something like this works for me:

  overlays = self: super: {
    danieldk = nixpkgs.legacyPackages.${"x86_64-linux"}.callPackage danieldk {};
  configuration = { pkgs, ...}: {
    nixpkgs.overlays = [ overlays ];
    imports = [ machines/mindbender.nix ];
in self.lib.homeManagerConfiguration {
      inherit configuration;

Where nixpkgs and danieldk are inputs and danieldk is not a flake (flake = false;).

tl;dr: I create a set of overlays to represent package sets and use the nixpkgs.overlays option of Home Manager. These packages then become visible using the given attributes e.g. (danieldk.alpino).

(I have only just started converting my home-manager configuration to a flake, so there may be better ways.)

in fact I use home-manager imperativelly, not with this module

my flake’s overlay show home-manager program and then I run the switch.
unfortunately, when it uses my overlay, it doesn’t know the reference to nixpkgs flake and home-manager

Olmo Kramer via NixOS Discourse writes:

Maybe you have to set home-manager.useGlobalPkgs = true; to
make Home Manager use the correct pkgs? I have it in my config
at least, and with the setup as described above I have no
issues, and I do not have to hardcode the path to the nix store.

I had to do this for it to pick up on my importing of the emacs
overlay0, but after that it was a breeze. I think you can also
configure home-manager's nixpkgs separately as well, but for
user instances I wouldn’t bother.

Not sure if this was a reply to my reply, because discourse says so. I also don’t use home-manager as module in my NixOS configuration.

Though with flakes I don’t use the home-manager command, but rather

$ nix build .#whateverOutput.activationPackage

(Of course, you could make activationPackage the output, just wanted to make this explicit.)

And then use the activation script in result/.

and this activationPackage is the value of a call to homeManagerConfiguration ?

It returns an attribute set, of which activationPackage is one of the attributes:

BTW, I use this function:

    lib = {
      homeManagerConfiguration = {
        , system ? "x86_64-linux"
        , homeDirectory ? "/home/daniel"
        , username ? "daniel"
        , pkgs ? nixpkgs.legacyPackages.${system}
        , check ? true }:
      import "${home-manager}/modules" {
        inherit check pkgs;

        configuration = { ... }: {
          imports = [ configuration ];
          home = { inherit homeDirectory username; };

Thank you I understand better how it is supposed to work now

Unfortunately I didn’t succeed to make it work completely.

Basically, my home-manager configuration is depending on my local flake’s overlay. I know how to apply overlays for legacy nixos configuration, and channels, but I can’t write the expression to combine inputs.

what I would like is some expression equivalent to:

combined = import nixpkgs { overlays = [ self.overlay otherOverlay ] };

Sadly, this line is not working

1 Like

Finally succeeded to compose my nixpkgs input + my local overlay (which propagated various things from other inputs). For those interested, here is my current solution:

combinedPackages = nixpkgs // flake-utils.lib.eachDefaultSystem (system: {
    legacyPackages = import nixpkgs {
      inherit system;
      config.allowUnfree = true;
      overlays = [ self.overlay ];

Then I just use combinedPackages as if it was nixpkgs !


Hi @freezeboy, as a beginer on NixOS, i want to start directly with best pratice, so i start using Flakes, but it’s hard. Do you have repo or something where we could learn from your exp ? Thx

1 Like

Nothing public at the moment, but after some cleaning, I could give it a try, it may help others and also I could receive some comments

Good idea :+1:

1 Like

Found some time to do it, the result is here

1 Like

Here’s my config using deploy-rs and github actions.

What doesn’t work right now:

  • building with nix-build-uncached (it doesn’t seem to do anything) (nix flake check and deploy works on my computer)
  • including my secrets git submodule conditionally (it’s ignored because it’s not indexed I think)

I’m guessing updating the flake.lock file must work but I didn’t test yet since I’m still using a non-master branch on github.

Thanks, also found this, we are on the road for an awesome-flakes github page :slight_smile: