Several environment.systemPackages in configuration.nix

Hi,

I want to organize correctly my configuration.nix file.

For that, I want to declare, for example, everything that touches gnome at the same place, so

services.xserver.displayManager.gdm.enable = true;
  services.xserver.desktopManager.gnome.enable = true;
  environment.systemPackages = [
    pkgs.gnomeExtensions.dash-to-dock
    pkgs.gnome.gnome-tweaks
  ];

but I also have later in the file another

environment.systemPackages = with pkgs; [
    vim
  ];

and it seems that the 2 interfers as I have when rebuilding

error: attribute 'environment.systemPackages' already defined at /etc/nixos/configuration.nix:47:3

How to deals with this ? Should I create a gnome.nix file that I call in my configuration file so that the 2 environment.systemPackages coexist?

1 Like

It worked by adding gnome.nix at the initial imports of my config file

imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      ./gnome.nix
    ];

Again, if I use imports after in the file I have an error that it is already defined. How to deals with this ? If it is possible.

This is just how attribute sets work In Nix: “An attribute name may only occur once.” This is because Nix does not have the concept of merge semantics (that is handled by NixOS module system) so Nix would not know how to handle the attribute conflict.

If you want to split your configuration into multiple sections with overlapping option definitions, you have the following options:

  • Move the conflicting modules to separate files and import them, as you did.

  • If you want to avoid separate files, you can inline a module expression and import it directly instead of loading it from a file first:

    { pkgs, ... }
    {
      imports = [
        ({ pkgs, ... }: {
          environment.systemPackages = [
            pkgs.vim
          ];
        })
      ];
    
      environment.systemPackages = [
        pkgs.gnomeExtensions.dash-to-dock
        pkgs.gnome.gnome-tweaks
      ];
    }
    
  • Alternately, merge multiple attribute sets inside a single module using the module system’s lib.mkMerge:

    { pkgs, ... }
    
    lib.mkMerge [
      {
        environment.systemPackages = [
          pkgs.vim
        ];
      }
    
      {
        environment.systemPackages = [
          pkgs.gnomeExtensions.dash-to-dock
          pkgs.gnome.gnome-tweaks
        ];
      }
    ]
    

I would probably go with multiple modules split into separate files since that is the cleanest.

3 Likes

Thanks a lot for your answer !

From the options you give, and as you say, I will stay with the split solution. It is the one that fit most what I expect.

The first question (and issue) comes from that I wanted to separate in the configuration file before splitting in multiple file.

Then the above solution with mkMerge seems the best solution no?

Well, as first step yes.

But, separate files is the one I choose finally.

Another thought : does this also works with user package like myuservscodium.nix :

{ pkgs, lib, ...}: {
  users.users.myuser = {
    packages = with pkgs; [
      vscodium
    ];
  };
}

if I want for example to install codium in user package and set it with vscode-with-extensions.override like here ?

And other question : is it possible to declare vscodium in my users.users.myuser packages and define the settings in another file ? Following doc example I would have codiumsettings.nix

users.users.myuser = with pkgs; [
  (vscode-with-extensions.override {
    vscode = vscodium;
    vscodeExtensions = with vscode-extensions; [
      bbenoist.nix
      ms-python.python
      ms-azuretools.vscode-docker
      ms-vscode-remote.remote-ssh
  })
];

Does the line vscode = vscodium actually install the package or is just a shortcut to replace vscodium by vscode for the management of the extensions ?

Again, it is a matter on how to set/organize files.

I see no reasons why this would fail.

Sure. You can use the import directive, like just copy/paste the part of the code you want to move in a new file, and use import ./thatfile.nix. There is just one subtlety : if you code uses some variable defined earlier, you need to forward them to the code. For that, just add {your, list, of, previously, defined, variables, you, need} : yourcode (if you used with XXX you might need to retype with XXX in your file), and call it like import ./thatfile.nix {inherit your list of previously defined variables you need;};. For instance if you want to move the whole vscode part, you can do something like:

users.users.myuser = with pkgs; [ (import ./myvscode.nix {inherit pkgs;}) ];

in myvscode.nix:

{pkgs }:
with pkgs; (vscode-with-extensions.override {
  vscode = vscodium;
  vscodeExtensions = with vscode-extensions; [
    bbenoist.nix
    ms-python.python
    ms-azuretools.vscode-docker
    ms-vscode-remote.remote-ssh
})

if you want to read more, see Nix language basics — nix.dev documentation and Nix Pills | Nix & NixOS

1 Like

Thanks ! Very clear !

I also see that in the preamble of your .nix file you only call pkgs, and in the main preamble of configuration.nix there is pkgs, lib and config (if I am correct, I am not on my nixos right now, not sure for lib).

So I guess pkgs is when you call pkgs. :exploding_head:

Is config to be used with boot options, or services for example ?

Also do not forget that in Nix, list items are separated by spaces so you need to use parentheses:

users.users.myuser = with pkgs; [ (import ./myvscode.nix {inherit pkgs;}) ];

1 Like

Ahah yes, this file is basically representing a function that will be called with inherit, where the first {…}: lists all inputs. So if you only need pkgs, you can just put pkgs. If later you need lib/config/… just add them, and make sure to add them in the inherit command to forward the pkgs/lib/… from the configuration.nix to the thatfile.nix.

Good point, just fixed it. That’s what happens when not testing a code :stuck_out_tongue:

config contains the configuration of all your system. So you can use it to read the value of a configuration defined in another module, or, if you create your own module and import it with imports = [], you can use config to check what value were enabled by the user, like mkIf config.services.yourservice.enable {…}.

What you call a module a just a .nix file ?

I have been trying around my .nix user profile file, and

{ pkgs, ...}: {
  users.users.myuser = {
      isNormalUser = true;
      extraGroups = [ "wheel" "networkmanager" ];
      packages = with pkgs; [
        firefox
      ];
    };
  }

worked whereas

{ pkgs }: {
  users.users.myuser = {
      isNormalUser = true;
      extraGroups = [ "wheel" "networkmanager" ];
      packages = with pkgs; [
        firefox
      ];
    };
  }

does not. What does ... stand for ?

I guess I have to dive into the tutorial.

Ok, I have one part of the answer :slight_smile:

FUNCTIONS

With additional attributes allowed

{ a, b, ...}: a + b

A module is, very roughly speaking, a way to split your configuration in multiple files, allowing this file to add any configuration or even create new options that may be called inside other modules (technically speaking your configuration.nix is already a module. Modules are typically imported via imports = [ ./yourModule.nix]. If you pay attention you already have one such module, the one configuring hardwares.

More precisely, a module is a function (often, indeed, in a separate nix file) that outputs an attribute set containing imports, config and options to import other modules, add new configuration options, or configure existing options. If you do not create new options, you can even put everything from config at the root of the set. You can read more in NixOS modules - NixOS Wiki