Declarative package management for normal users

Hi. One of the first things that amazes me about Nixos is how convenient it is to manage packages from a .nix file. But this is restricted only to the super user. To install my personal packages, packages that would not make sense to be available in the whole system, I have to use nix-env in the terminal, and it’s a pain in the ass when there are many packages.

It would be great to have something like enviroment.userPackages; with pkgs; [];
It would also be great to have something nixos-rebuild for normal users.


There are multiple ways of managing declarative profiles. 1) Attrset, compatible with imperative use of nix-env lnl-overlay.nix · GitHub ; 2) buildEnv, providing more control over the paths that are linked into the profile Expression for a buildEnv-based declarative user environment. · GitHub ; 3) home-manager, providing nixos-like config for your ~ GitHub - nix-community/home-manager: Manage a user environment using Nix [maintainer=@rycee]


All approaches @Mic92 described are indeed declarative, however you have to edit file to add new package.

Would you give up imperative nix-env --install behavior for this? If not, then read next.

I’ve written a small (50 lines) bash script, that replaces 50% of nix-env. Here it is Imperative nix-env rewrite (so it becomes declarative) · GitHub. Add an alias (declaratively in NixOS or in ~/.profile)

alias nix-env=/path/to/script

It replaces nix-env with a thing, that supports 3 commands: install, uninstall and list.

$ nix-env list

$ nix-env uninstall calibre
Not installed

$ nix-env install calibre
scheduled install'n'update
error: undefined variable 'calibre' at /home/danbst/.config/nixpkgs/declarative-env.nix:7:1

$ nix-env install nixpkgs.calibre
scheduled install'n'update
replacing old 'declarative-collection'
installing 'declarative-collection'
building '/nix/store/2cnhxqwxl79qj81mfkrqpl7xr361i3z1-user-environment.drv'...
created 5054 symlinks in user environment

$ nix-env install '(nixpkgs.htop.overrideAttrs (_: { name = "my-override"; }))'
scheduled install'n'update
replacing old 'declarative-collection'
installing 'declarative-collection'
these derivations will be built:
building '/nix/store/fbggzpbs91ryysmdzc131vvkg8vm14yz-declarative-collection.drv'...
building '/nix/store/wpb2w7vj3cq4lsl1syac7lhndahqc64r-user-environment.drv'...
created 5054 symlinks in user environment

$ nix-env list
(nixpkgs.htop.overrideAttrs (_: { name = "my-override"; }))

# Original nix-env is available as \nix-env

There are 3 distinctions from nix-env here (besides completely new CLI):

  • packages are identified by their attribute, instead of “name”. Just like in nix 2.0
  • it saves it’s installed packages state in simple format: line per package (original nix-env builds a maniphest.nix with lots of metadata, but most importantly, it doesn’t save attribute name in maniphest)
  • as a buildEnv wrapper, it maintains consistency among all installed pacakges (can upgrade/downgrade some unrelated package). Same behavior as for nixos-rebuild

In fact, nix-env list is an alias to cat ~/.config/nixpkgs/declarative. This list is used to build env:

$ cat /home/danbst/.config/nixpkgs/declarative-env.nix
       │ File: /home/danbst/.config/nixpkgs/declarative-env.nix
   1   │ let nixos = import <nixos> { }; in
   2   │ let nixpkgs = import <nixpkgs> { }; in
   3   │ let _pkgs = import <nixpkgs> { }; in
   4   │ rec { _paths = [
   5   │ nixpkgs.calibre
   6   │ (nixpkgs.htop.overrideAttrs (_: { name = "my-override"; }))
   7   │              ];
   8   │       env = _pkgs.buildEnv {
   9   │       name = ''declarative-collection'';
  10   │       paths = _paths;
  11   │ }; }
  12   │ # Updated successfully!

And installed atomically with original nix-env. Pretty much declarative’n’imperative now! (I have a feeling that people have done this before…)


Thanks for the info, but the ways you show me are from third parties. They are not official. I imagined that there would be a way similar to how some packages can be configured in .nixpkgs / conf.nix.

I think the best thing will be that or report as a suggestion on Github.

There exists an option for configuration.nix, but it requires root to apply changes:<name%3F>.packages

The “official” way to support declarative user package collection is indeed via buildEnv:

(note that packageOverrides was superseded by overlays recently)


I use the -f and -r flags of nix-env , along with a custom configuration file, for this purpose.

I have a file, ~/.config/nixpkgs/packages.nix, with contents like the following:

{ pkgs ? import <nixos> {}}:
with pkgs;
  # Packages go here

I then run nix-env -irf ~/.config/nixpkgs/packages.nix to make my profile match what is declared in that file. I leave it to you decide which set of tradeoffs you think are worthwhile.


perhaps some more or less; relevant links discussion @ Pocnix: a proof-of-concept Nix CLI


Thanks for the info, but the ways you show me are from third parties. They are not official.

Welcome to Nix. :stuck_out_tongue:

The “third parties” are often the same people who make NixOS, and a lot of tools that are integral to Nixpkgs don’t live in the NixOS organisation. It’s just the way things are. Often there’s no “official” way because there’s no point — somebody else has already addressed the need.

1 Like

Thanks for this clarification! Too bad that the Nixpkgs manual only mentions this at the very end of the Overlays chapter, way after discussing declarative package management:

Overlays are similar to other methods for customizing Nixpkgs, in particular the packageOverrides attribute described in Section 6.5, “Modify packages via packageOverrides. Indeed, packageOverrides acts as an overlay with only the super argument. It is therefore appropriate for basic use, but overlays are more powerful and easier to distribute.

Would love to hear similar insights into the Nix flakes proposal; I’ve seen a lot of activity on Github around it, but most are over my head yet.