Where does pkgs come from?

Alternatively:

First of all, what is pkgs?

I'm using pkgs to refer to an instantiated package set from a copy of nixpkgs.

In the most basic case, you just need the nixpkgs sources as well as a system (e.g. "x86_64-linux") which will be used for both building the packages and running them later.

In more complex cases, you might have different systems for building vs. running (cross-compiling). Or you might configure some options on nixpkgs to allow using packages that aren't allowed by default (unfree or insecure packages).

Either way, you get a giant attribute set of packages, and that's what I'm calling pkgs since that's the most common variable name given to it.

Depending on how you are using Nix, there may be a variety of ways to get/create a pkgs instance, so let's go through what those are.

Imported from nixpkgs sources

The most universal way to create a pkgs instance is to import it from a copy of the nixpkgs sources.

For instance, if you have a nixpkgs channel set up on your system (or have a nixpkgs entry on your NIX_PATH some other way), you could do it like this:

let
  pkgs = import <nixpkgs> {};
in ...

There's nothing magic about that import {} expression. gives a path to whatever nixpkgs points to on the NIX_PATH, which should be the root directory of the nixpkgs sources. Nixpkgs includes a default.nix, so you can import the directory to evaluate that default.nix file. That file returns a function that creates the pkgs instance, so we pass {} as an argument to that function.

When we create it this way, it uses builtins.currentSystem as the system for the packages. We can specify the system explicitly though:

let
  pkgs = import <nixpkgs> {
    system = "aarch64-linux";
  };
in ...

Configuration settings can be specified under the config key:

let
  pkgs = import <nixpkgs> {
    system = "aarch64-linux";
    config.allowUnfree = true;
  };
in ...

This kind of import works no matter where you're getting your nixpkgs sources from. For instance, maybe you've got two channels: nixpkgs pointing at a stable release channel, and nixpkgs-unstable for a few packages you need newer versions of from unstable. To get access to those unstable packages, you can import from that channel instead:

let
  pkgs = import <nixpkgs-unstable> {};
in ...

Or maybe you want to reference a specific nixpkgs commit without relying on getting it from the NIX_PATH. You can import your pkgs from a fetched tarball instead:

let
  nixpkgs = builtins.fetchTarball {
    url = "https://github.com/NixOS/nixpkgs/archive/3389f23412877913b9d22a58dfb241684653d7e9.tar.gz";
    sha256 = "0wgm7sk9fca38a50hrsqwz6q79z35gqgb9nw80xz7pfdr4jy9pf8";
  };
  pkgs = import nixpkgs {};
in ...

And finally, this also works with flakes. You can import a flake input pointing at the nixpkgs repository in the exact same way:

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

  outputs = { nixpkgs, ... }:
    let
      pkgs = import nixpkgs {
        system = "x86_64-linux";
        config.allowUnfree = true;
      };
    in {
      ...
    };
}

This works because while flake inputs are attribute sets with the outputs of the flake, they also include an outPath attribute that contains the path in the Nix store where that input's sources live. Attribute sets with an outPath can be used in places where paths are expected (packages also have an outPath).

The legacyPackages flake output

The flake.nix for nixpkgs also exposes basic pkgs instances for various systems. These are roughly equivalent to importing nixpkgs like above and not passing any config.

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

  outputs = { nixpkgs, ... }:
    let
      pkgs1 = import nixpkgs {
        system = "x86_64-linux";
      };
      pkgs2 = nixpkgs.legacyPackages.x86_64-linux;
    in {
      ...
    };
}

In this example, pkgs1 and pkgs2 will have the same value.

There's no way to set config options on a pkgs created this way, though. If you need to set configuration on nixpkgs, you'll need to use the import method instead.

Implicit pkgs in a NixOS config

Finally, if you're written NixOS system configuration, you're used to being able to take an argument called pkgs in your modules and use that for your packages. Where does that come from?

It's created for you when evaluating the configuration. Where it gets the nixpkgs sources from depends on whether you're using flakes or not:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
    nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { nixpkgs-unstable, ... }: {
    nixosConfigurations.bulbasaur = nixpkgs-unstable.lib.nixosSystem {
      modules = [
        ({ pkgs, ... }: {
          environment.systemPackages = [ pkgs.git ];
        })
      ];
    };
  };
}

This implicit pkgs instance can be configured through options in the NixOS configuration itself. These options are all under the nixpkgs key:

These options only affect the pkgs instance that is created automatically. If you create any yourself, either with import or legacyPackages, they are not affected by these options.

Proxy Information
Original URL
gemini://midna.dev/nix/faq/pkgs.gmi
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
19.767284 milliseconds
Gemini-to-HTML Time
0.574272 milliseconds

This content has been proxied by September (UNKNO).