How to install a local flake

I’m trying to add a local flake-based derivation I made to my configuration, but I’m getting an error. Basically I call the flake, then add it to my systemPackages:

{ config, pkgs, ... }:
let
  my_thing = pkgs.callPackage ./derivations/my_thing/flake.nix;
in {
  environment.systemPackages = with pkgs; [
    my_thing
  ];
}

However, this doesn’t work, and gives a cryptic error:

error: A definition for option `environment.systemPackages.[definition 1-entry 8]' is not of type `package'. Definition values:
       - In `/nix/store/znpvasx8xpahxhyfzgr6aawi8r8v2dcg-source/installed-software.nix': <function>

So maybe I need to use my_thing = pkgs.callPackage ./derivations/my_thing/flake.nix {};?

Nope.

error: attempt to call something which is not a function but a set

       at /nix/store/yar8836x2ckxvmdw5d72j58p5lqyp6qm-source/lib/customisation.nix:69:16:

           68|     let
           69|       result = f origArgs;
             |                ^
           70|

All I want to do is install a locally defined derivation flake to my system. How do I do that?

3 Likes

of course not, callPackage works only on functions, because it needs to inspect its arguments, and the usual flake.nix doesn’t “export” one it’s an attrs usually.

If you don’t like the kinds of questions newbies ask, that’s fine; just steer clear of the “Learn” forum.

Posting condescending and unhelpful responses to questions ultimately does the Nix ecosystem a disservice by dissuading newcomers and reinforcing the gatekeeper reputation it already has.

11 Likes

I don’t think the “of course not” was intended to be condescending, though I can se why it would be perceived that way. Let’s chalk it up to the difficulty of communication and get back to your question :slight_smile:

Could you describe in a bit more detail what you’d like to achieve? This seems like an odd use case; flakes aren’t intended to be “called” like a function, they are by definition collections of a variety of things nix can operate on, among which functions.

Are you trying to call a function in the flake? Make use of a module in it? Install a package from it?

I appreciate those questions may be difficult to answer if you haven’t encountered them before, I’d recommend a read through the wiki on flakes: Flakes - NixOS Wiki

The “output schema” heading covers all the various things a flake can include, we’d need to know which of those you want to use to help. Most likely, you’ll also need to convert your system configuration to a flake, though in theory you can hack around that if you’re using a flake-compatible nix.

3 Likes

Hi, basically I have a flake that builds a package that I’d like to install in my running system (ultimately, it will be deployed in a container, as a daemon (with all the systemd goodness), but for now I just want a proof-of-concept that I can verify on my main system.

So for example, a trivial flake like:

{
  description = "A flake for building My Hello World";

  inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-21.05;
  inputs.flake-utils.url = github:numtide/flake-utils;

  outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
    let
      pkgs = nixpkgs.legacyPackages.${system};
      packageName = "myhello";
    in {
      packages.${packageName} = pkgs.stdenv.mkDerivation {
        name = "${packageName}";
        src = self;
        buildPhase = "gcc -o myhello ./myhello.c";
        installPhase = ''
          mkdir -p $out/bin
          install -t $out/bin myhello
        '';
      };
      defaultPackage = self.packages.${system}.${packageName};
    }
  );
}

Where the trivial myhello.c is:

#include <stdio.h>
int main() {
    printf("Hello My World\n");
    return 0;
}

I then want to add this package to my system’s config such that I can verify that everything works in a real system (file locations, runtime configurations etc) before I move on to making it a full github repo. I also want this for things that are intimate parts of the container in question, rather than separate repos.

For starters, I hope to be able to call nixos-rebuild switch --flake /etc/nixos and then be able to call myhello from the command line. I just need to get to this step and then I think I can handle the rest.

My system config is also a flake like so:

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.05";
  outputs = { self, nixpkgs }:
    let
      hostname = "xxxxxx";
    in {
      nixosConfigurations.${hostname} = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules =
          [ ({ pkgs, ... }: {
              system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;

              nix = {
                package = pkgs.nixFlakes;
                extraOptions = ''
                  experimental-features = nix-command flakes
                '';
                registry.nixpkgs.flake = nixpkgs;
              };

              ...

              imports = [
                ./hardware-configuration.nix
                ./installed-software.nix
              ];
            })
          ];
      };
    };
}

I’d be referencing myhello in installed-software.nix:

{ config, pkgs, ... }:

{
  nixpkgs.config.allowUnfree = true;

  environment.systemPackages = with pkgs; [
    htop
    telnet
    vim
    python
    p7zip
    unrar
    tree
    myhello
  ];
}
1 Like

I was going to say maybe you could try builtins.getFlake (it seems it is not documented). But since your system configuration is already a flake, you could just add your app flake as an input to your system flake and add it under _modules.args as an extra input to your modules:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.05";
    trivial-flake = {
      url = "path:<path-to-flake>";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, trivial-flake }:
    let
      hostname = "xxxxxx";
    in {
      nixosConfigurations.${hostname} = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        _module.args = { inherit trivial-flake; };

        modules =
          [ ({ pkgs, ... }: {
              system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;

              nix = {
                package = pkgs.nixFlakes;
                extraOptions = ''
                  experimental-features = nix-command flakes
                '';
                registry.nixpkgs.flake = nixpkgs;
              };

              ...

              imports = [
                ./hardware-configuration.nix
                ./installed-software.nix
              ];
            })
          ];
      };
    };
  }
# installed-software.nix
{ config, pkgs, trivial-flake, ... }:

{
  nixpkgs.config.allowUnfree = true;

  environment.systemPackages = with pkgs; [
    trivial-flake.defaultPackage
    htop
    telnet
    vim
    python
    p7zip
    unrar
    tree
    myhello
  ];
}

inputs.follows.nixpkgs = "nixpkgs" while adding trivial-flake as an input to the system flake is not a requirement, but it is done to reduce closure size by avoiding installing duplicate packages. I think if you produce an overlay instead of a package with your trivial flake, you could also get rid of it.

1 Like

This is indeed an option if you’re using a non-flake system configuration, but I’d consider that a hack - using a flake to describe the system too is better.

If you’re not fond of introducing an overlay into such a trivial flake, you can put that burden on the consumer and still not need _module.args:

# flake.nix
{
  <snip>
  outputs = { self, nixpkgs, trivial-flake }:
    let
      hostname = "xxxxxx";
      overlays = [ (final: prev: { myhello = trivial-packages.defaultPackage; }) ];
    in {
      nixosConfigurations.${hostname} = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";

        modules =
          [ ({ pkgs, ... }: {
                <snip>
                nixpkgs.overlays = overlays;
                <snip>
            })
          ];
      };
    };
  }

And while I’m here, let me recommend build-vm, which is probably better suited to testing things like this than installing directly to a dev machine: NixOS:nixos-rebuild build-vm - NixOS Wiki.

Hey great, thanks alot! I’ll give this a whirl later tonight!

Just a quick note: build-vm looks like it’s broken:

[karl@nas:~]$ nixos-rebuild build-vm
building the system configuration...

Done.  The virtual machine can be started by running /nix/store/333wh6kai1q2l2i7p45ndmirdv0iil03-nixos-vm/bin/run-nas-vm

[karl@nas:~]$ /nix/store/333wh6kai1q2l2i7p45ndmirdv0iil03-nixos-vm/bin/run-nas-vm
Formatting '/home/karl/nas.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=536870912 lazy_refcounts=off refcount_bits=16
Unable to init server: Could not connect: Connection refused
gtk initialization failed

You may need to enable virtualisation.libvirtd.enable on the host to enable virtualization in general; though I’d thought it gives a better error message if that fails.

Also some motherboards have KVM disabled by default, in which case you’d need to enable it in BIOS first.

@ksten, why don’t you simply declare your local flake as a flake input? This is the usual route to import flakes.

2 Likes

OK, I’ve switched over to defining a container to make things a little easier for now. This is the default container flake template from nix flake init -t templates#simpleContainer:

# cat <<'EOF'> flake.nix 
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.05";
    trivial-flake = {
      url = "path:/home/karl/myhello";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

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

    nixosConfigurations.container = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules =
        [ ({ pkgs, ... }: {
            boot.isContainer = true;
            system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;

            environment.systemPackages = with pkgs; [
            ];
          })
        ];
    };

  };
}
EOF

I can create this fine with nixos-container create x --flake .

The _modules.args approach doesn’t seem to work:

    nixosConfigurations.container = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      _modules.args = { inherit trivial-flake; }; # New line here
      modules =
        [ ({ pkgs, ... }: {
# nixos-container create x --flake .
host IP is 10.233.1.1, container IP is 10.233.1.2
error: anonymous function at /nix/store/afvflyac1s5f1y9sr7pvyxz644jl04k8-source/nixos/lib/eval-config.nix:11:1 called with unexpected argument '_modules'

       at /nix/store/afvflyac1s5f1y9sr7pvyxz644jl04k8-source/flake.nix:30:11:

           29|         nixosSystem = { modules, ... } @ args:
           30|           import ./nixos/lib/eval-config.nix (args // {
             |           ^
           31|             modules =
/run/current-system/sw/bin/nixos-container: failed to build container from flake '.'

The overlays approach also breaks:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.05";
    trivial-flake = {
      url = "path:/home/karl/myhello";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, trivial-flake }:
    let
      overlays = [ (final: prev: { myhello = trivial-flake.defaultPackage; }) ];
    in {
      nixosConfigurations.container = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules =
          [ ({ pkgs, ... }: {
              boot.isContainer = true;
              system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;

              nixpkgs.overlays = overlays;
              environment.systemPackages = with pkgs; [
                myhello
              ];
            })
          ];
      };
    };
}
# nixos-container create x --flake .
host IP is 10.233.1.1, container IP is 10.233.1.2
error: A definition for option `environment.systemPackages.[definition 1-entry 1]' is not of type `package'. Definition values:
       - In `<unknown-file>':
           {
             aarch64-darwin = <derivation /nix/store/mpj01car6jwhfmclfj1sb276ky9cp8z0-myhello.drv>;
             aarch64-linux = <derivation /nix/store/4biwbagrxq604r5dl8qfjyk4hafxyfrb-myhello.drv>;
             i686-linux = <derivation /nix/store/q3sl4fg37mhrfsl8a97sp158vd232dwa-myhello.drv>;
             x86_64-darwin = <derivation /nix/store/14bbynbsbjlzvg0jxxm9kavdiphd3l31-myhello.drv>;
           ...
(use '--show-trace' to show detailed location information)
/run/current-system/sw/bin/nixos-container: failed to build container from flake '.'

Also:

# nixos-container create x --flake . --show-trace
Unknown option: show-trace

However, if I do this, it works:

      overlays = [ (final: prev: { myhello = trivial-flake.defaultPackage.x86_64-linux; }) ];

Full working container flake.nix:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.05";
    trivial-flake.url = "path:/home/karl/myhello";
  };

  outputs = { self, nixpkgs, trivial-flake }:
    let
      overlays = [ (final: prev: { myhello = trivial-flake.defaultPackage.x86_64-linux; }) ];
    in {
      nixosConfigurations.container = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules =
          [ ({ pkgs, ... }: {
              boot.isContainer = true;
              system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;

              nixpkgs.overlays = overlays;
              environment.systemPackages = with pkgs; [
                myhello
              ];
            })
          ];
      };
    };
}

Is this the “best practice” way to do it?
Is there a way to avoid having to specify the arch?
Would this also be how it’s done with a flake that’s hosted on github somewhere?

The Arch is required otherwise nixos won’t know what system to build packages for. If you want to build it for more than one system, you could use flake-utils to generate the same config for multiple systems, or just do something similar manually, although nixosConfigurations don’t typically expect a system spaced output [see NixOS/nix#4556]

1 Like

The argument should be _module.args, singular.

It should also be set under modules, see here: https://github.com/NixOS/nixpkgs/blob/0e575a459e4c3a96264e7a10373ed738ec1d708f/lib/modules.nix#L85

That is, it’s a module option, not an argument to the configuration eval function.

More or less. I think the best practice is currently still not to use flakes in the first place, since it’s an unstable feature. If you do use them, this plus flake-utils to parameterize the arch is probably the “best practice”.

Yes, though typically they would provide an overlay, so you don’t need to worry about writing your own overlay :slight_smile:

Ah ok, cool. But it seems that flakes are where everything is going in the end, so I’d like to master that.

Given where things are in the “myhello” flake now, how would I do all the right things with it to host it for others? (overlays, anything else) This is what the “myhello” flake currently looks like, as the file flake.nix inside the dir /home/karl/myhello:

{
  description = "A flake for building My Hello World";

  inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-21.05;
  inputs.flake-utils.url = github:numtide/flake-utils;

  outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
    let
      pkgs = nixpkgs.legacyPackages.${system};
      packageName = "myhello";
    in {
      packages.${packageName} = pkgs.stdenv.mkDerivation {
        name = "${packageName}";
        src = self;
        buildPhase = "gcc -o myhello ./myhello.c";
        installPhase = ''
          mkdir -p $out/bin
          install -t $out/bin myhello
        '';
      };
      defaultPackage = self.packages.${system}.${packageName};
    }
  );
}

Since you’re using flake-utils, just use the simpleFlake function provided by that :slight_smile:

Set the overlay arg to something like:

final: prev: { myhello = {
    myhello = self.defaultPackage; # Since flake-utils uses legacyPackages this works, I think
    };
}

That’d set up a namespace in pkgs for your flake, and add the myhello package to it. I’d consider that good practice, just dumping packages into the global scope (unless you’re explicitly overriding something) is probably not great. But with just one package it’s your call.

At that point it’s up to the user whether to use your provided package, or to use your overlay.

I’d recommend reading through the flake-utils source if you want to find out how all that works so you can do it manually, it’s fairly straightforward.

Personally, I also like adding apps, because it makes nix run useful, but that’s not provided by simpleFlake. Maybe a worthwhile thing to figure out if you want to get to the bottom of how it all fits together. The ecosystem is very much not yet figured out, so you have rough edges like that :wink:

Also, if you really want to know how the nixosConfigurations bit works, read the function in nixpkgs’ flake.nix, and read through the library functions it calls. When I first learned flakes that helped me a lot.

1 Like

Sorry, I don’t quite follow… where would this overlay code go?
Also what is apps?

Read the flake-utils documentation for how their functions work, they’ll probably do a better job explaining than I do, and there’s even an example linked.

I’d also suggest reading the wiki page attentively if you’d like to learn about flakes :wink: The output schema lists all the things flakes can do, with some minimal description of what they mean; if you want to know more it’s worth just trying the options out.

The basic gist for apps is that they can be run like nix run flake-url#app, it’s simply a way to link a packaged binary to a name in the nix world. For example, I often use nix run nixpkgs#unzip -- <some-zip-file>, because I stubbornly refuse to install unzip despite needing it all the time.

I believe legacy packages are automatically linked to an app with the same name as the key in the attrset that describes them, which is why unzip works for nixpkgs, but e.g. nix run nixpkgs#lsusb won’t.