"nix profile" in combination with declarative package management

Hello all!

I have a nice declarative package management setup as described in the nixpkgs-manual and base on package overrides. However now I got forced into using nix profile instead of nix-env. In contrast to before I get now the following error:

% nix-env --install --attr nixpkgs.myPackageCollection
error: profile '/nix/var/nix/profiles/per-user/<user>/profile' is incompatible with 'nix-env'; please use 'nix profile' instead

O.K. I am fine with this, I need to use flakes for now. But how can I use my declarative declaration using nix profile? I just tried:

nix profile install 'nixpkgs#myPackageCollection'`
error: flake 'flake:nixpkgs' does not provide attribute 'packages.x86_64-linux.myPackageCollection', 'legacyPackages.x86_64-linux.myPackageCollection' or 'myPackageCollection'

Please give me a hint how to do the command.

Thank you in advance!

(Btw.: I was suprised the first time I installed a flake, nix-env stopped working.)

1 Like

You can use flakes without having nix profile activated.

And I personally still live with a nix-env compatible set up, as that works better with Home-Manager.

As far as I understand the situation, you now need to set up your own flake which pulls nixpkgs as an input and install your collection from that.

1 Like

So long as you completely avoid using nix profile, you can technically keep using nix-env if you want to, though it’ll require some jerry-rigging to be able to access flake outputs from it.

However, nix-env is broken in some odd ways, which is why switching to nix profile is definitely the better choice if you can do it.

Flakes, however, are built around the idea that nix code evaluation doesn’t have access to any state outside the flake and its inputs, so, among other things, that means things in ~/.config/nixpkgs are not read. Rather than an overlay like that, I’d recommend making a small flake that exports a similar pkgs.buildEnv environment. At barebones, it could just be a flake.nix like this: (this assumes an x86_64-linux system, edit as appropriate)

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05";
  outputs = {self, nixpkgs, ...}: let
    pkgs = nixpkgs.legacyPackages.x86_64-linux;
  in {
    packages.x86_64-linux.myPackageCollection = pkgs.buildEnv {
      ...
    };
  };
}
1 Like

Thank you for your quick response! I used nix profile to install a flake due to the description on the flakes-wiki. Do you know how can I install a flake via nix-env?

@tejing Thank you as well for your precise response. That looks like a great solution!

I would do it through the declarative package collection rather than individually.

I generally do not install anything imperatively (well, except for steam games and VScode plugins).

With nix-env, this would require some boilerplate using flake-compat

So long as flakes are enabled, you shouldn’t need flake-compat. builtins.getFlake would do.

1 Like

I have another question regarding installing the flake now that I defined using your template: When I try to list in myPackageCollection some binaries, e.g.cowsay (as a toy-example) and install afterwards the flake containing it, then I can not find the binary:

% which cowsay         
cowsay not found

What do I need to do here? I somehow fear that I need to add an app-entry for each executable. If that’s true, can it be done in an elegant way?

Can you please share how you wrote the flake and how you installed it?

Here is the file content of a “flake.nix”:

{
  description = "A declarative system installation";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.05"; # also possible: `nixos-unstable`
  };

  outputs = { self, nixpkgs }:
    let
      supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];

      # Generate a user-friendly version number.
      version = builtins.substring 0 8 self.lastModifiedDate;

      # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
      forAllSystems = nixpkgs.lib.genAttrs supportedSystems;

      # Nixpkgs instantiated for supported system types.
      nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
    in {
      packages = forAllSystems (system:
        let
          pkgs = nixpkgsFor.${system};
        in {
          default = self.packages.${system}.myPackageCollection;
          myPackageCollection = # libs and clis
            let
              pkgs = nixpkgs.legacyPackages.${system}; # here we need just legacy packages
            in pkgs.buildEnv {
              name = "myPackageCollection";
              paths = with pkgs; [
                # ToDo
              ];

               extraOutputsToInstall = [ "man" "doc" ];
            };
          myPackageCollection2 = # libs and clis
            let
              pkgs = nixpkgs.legacyPackages.${system}; # here we need just legacy packages
            in pkgs.buildEnv {
              name = "myPackageCollection2";
              paths = with pkgs; [
                cowsay
              ];

               extraOutputsToInstall = [ "man" "doc" ];
            };
        }); # packages
    }; # outputs
}

I installed it via nix profile install . from the same directory of the “flake.nix”-file.

. installs the default package, the default package is an alias for myPackageCollection which again is empty, you probably wanted to install .#myPackageCollection2 which indeed contains cowsay.

1 Like

Ah, thank you! Great!

One last thing: Is there a way to install every package in a flake? I looked up in the manual but failed to find the solution.
The following did not work:

nix profile install '.#*'                   
error: flake 'path:/home/<user>/.config/nixpkgs' does not provide attribute 'packages.x86_64-linux.*', 'legacyPackages.x86_64-linux.*' or '*'

Alternatively, is it possible to write a default-package that just pulls in any other package (as something similar to a dependency in traditional package-management like rpm or apt)?

default = pkgs.buildEnv {
  name = "everything";
  paths = builtins.attrValues (pkgs.lib.filterAttrs (name: _: name != "default") self.packages.x86_64-linux);
}

Might work, though it is untested.

1 Like

Great, I got the idea!