Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Creating and Building a Local Package within a Nixpkgs Clone

While an actual submission to Nixpkgs involves more steps, this chapter demonstrates the fundamental pattern for creating a package. Every package recipe is a file that declares a function. This function takes the packages dependencies as argument.

In this example we'll make a simple package with coreutils and build it. Demonstrating the process of building and testing a local package.

Clone Nixpkgs

First, we'll clone Nixpkgs and try to find a good spot to put our package. We're just building a test package so nixpkgs/pkgs/misc could be a good place to start. We'll call our package testPackage.

cd ~
mkdir src && cd src
git clone https://github.com/NixOS/nixpkgs.git
cd nixpkgs/pkgs
ls # Try to find a catagory that your pkg fits in
╭────┬────────────────┬──────┬─────────┬─────────────╮
│  # │      name      │ type │  size   │  modified   │
├────┼────────────────┼──────┼─────────┼─────────────┤
│  0 │ README.md      │ file │ 50.6 kB │ 2 hours ago │
│  1 │ applications   │ dir  │   398 B │ 2 hours ago │
│  2 │ build-support  │ dir  │  2.5 kB │ 2 hours ago │
│  3 │ by-name        │ dir  │  2.9 kB │ 2 hours ago │
│  4 │ common-updater │ dir  │   286 B │ 2 hours ago │
│  5 │ data           │ dir  │    82 B │ 2 hours ago │
│  6 │ desktops       │ dir  │   164 B │ 2 hours ago │
│  7 │ development    │ dir  │   882 B │ 2 hours ago │
│  8 │ games          │ dir  │  1.5 kB │ 2 hours ago │
│  9 │ kde            │ dir  │   116 B │ 2 hours ago │
│ 10 │ misc           │ dir  │   390 B │ 2 hours ago │
│ 11 │ os-specific    │ dir  │    42 B │ 2 hours ago │
│ 12 │ pkgs-lib       │ dir  │    68 B │ 2 hours ago │
│ 13 │ servers        │ dir  │  1.0 kB │ 2 hours ago │
│ 14 │ shells         │ dir  │    46 B │ 2 hours ago │
│ 15 │ stdenv         │ dir  │   178 B │ 2 hours ago │
│ 16 │ test           │ dir  │   702 B │ 2 hours ago │
│ 17 │ tools          │ dir  │   342 B │ 2 hours ago │
│ 18 │ top-level      │ dir  │  2.3 kB │ 2 hours ago │
╰────┴────────────────┴──────┴─────────┴─────────────╯

Ad-hoc semi-regular structure, if you need to make a new package we first make a directory with the name of the package and a default.nix in said directory:

❗ NOTE: In this example we will use the misc directory, it is now recommended to use the by-name directory. Explained further down.

Create your Package directory and a default.nix

cd misc
mkdir testPackage && cd testPackage
hx default.nix
# default.nix
{
  runCommand,
  coreutils,
}:
runCommand "testPackage" {
  nativeBuildInputs = [
    coreutils
  ];
} ''

  echo 'This is a Test' > $out
''

Now we need to add our testPackage to all-packages.nix

cd pkgs/top-level
hx all-packages.nix

all-packages.nix is a centralized module that defines all available package expressions.

We'll add our package in the list alphabetically:

# all-packages.nix
# `/msc` # editor search inside file
# Scroll down to t's
# snip ...
termusic = callPackage ../applications/autio/termusic { };

# we add our package here
testPackage = callPackage ../misc/testPackage { };

tfk8s = callPackage ../applications/misc/tfk8s { };
# snip ...

callPackage is a core utility in Nixpkgs. It takes a Nix expression (like our default.nix file, which defines a function) and automatically provides the function with any arguments it declares, by looking them up within the pkgs set (or the scope where callPackage is invoked). This means you only need to list the dependencies your package needs in its default.nix function signature, and callPackage will "inject" the correct versions of those packages. This is what the callPackage Nix Pill demonstrates at a lower level.

Understanding pkgs/by-name/ and other locations

Nixpkgs uses different conventions for package placement:

  • Older categories (e.g., pkgs/misc/, pkgs/applications/): Packages within these directories typically use default.nix as their definition file (e.g., pkgs/misc/testPackage/default.nix). These packages are NOT automatically included in the top-level pkgs set; they must be explicitly added via a callPackage entry in pkgs/top-level/all-packages.nix. This is the method demonstrated in this chapter for our testPackage.

  • The new pkgs/by-name/ convention: This is the preferred location for new packages.

    • Packages here are placed in a directory structure like pkgs/by-name/<first-two-letters>/<package-name>/.

    • Crucially, their main definition file is named package.nix (e.g., pkgs/by-name/te/testPackage/package.nix).

    • Packages placed within pkgs/by-name/ are automatically discovered and exposed by Nixpkgs' top-level pkgs set. They do not require a manual callPackage entry in all-packages.nix. This results in a more modular and scalable approach, reducing manual maintenance.

❗ : While this example uses pkgs/misc/ to demonstrate explicit callPackage usage, when contributing a new package to Nixpkgs, you should nearly always place it within pkgs/by-name/ and name its definition file package.nix.

Previously, packages were manually added to all-packages.nix. While this is no longer needed in most cases, understanding the old method provides useful context for troubleshooting legacy configurations or custom integrations.

Try Building the Package

Move to the root directory of Nixpkgs:

cd ~/src/nixpkgs

Try building it:

nix-build -A testPackage
this derivation will be built:
this derivation will be built:
  /nix/store/yrbjsxmgzkl24n75sqjfxbpv5cs3b9hc-testPackage.drv
building '/nix/store/yrbjsxmgzkl24n75sqjfxbpv5cs3b9hc-testPackage.drv'...
/nix/store/3012zlv30vn6ifihr1jxbg5z3ysw0hl3-testPackage

runCommand is a simple builder, it takes 3 arguments. The first is the package name the second is the derivation attributes, and the third is the script to run.

cat ~/src/nixpkgs/result
───────┬──────────────────────────────
       │ File: result
───────┼──────────────────────────────
   1   │ This is a Test
───────┴──────────────────────────────
nix-instantiate --eval -A testPackage.meta.position
"/home/jr/src/nixpkgs/pkgs/misc/testPackage/default.nix:6"

Tools like nix search and the Nixpkgs website use the meta information for documentation and discoverability. It can also be useful for debugging and helps to provide better error messages. The above command shows that the meta.position attribute points to the file and line where the package definition begins, which is very useful for debugging.

Typically a file will have a meta attribute that looks similar to the following:

meta = with lib; {
    homepage = "https://www.openssl.org/";
    description = "A cryptographic library that implements the SSL and TLS protocols";
    license = licenses.openssl;
    platforms = platforms.all;
} // extraMeta;

For example, the following shows how Nix is able to discover different parts of your configuration:

Launch the nix repl and load your local flake:

cd /src
nix repl
nix-repl> :lf nixpkgs
nix-repl> outputs.legacyPackages.x86_64-linux.openssl.meta.position
"/nix/store/syvnmj3hhckkbncm94kfkbl76qsdqqj3-source/pkgs/development/libraries/openssl/default.nix:303"
nix-repl> builtins.unsafeGetAttrPos "description" outputs.legacyPackages.x86_64-linux.openssl.meta
{
  column = 9;
  file = "/nix/store/syvnmj3hhckkbncm94kfkbl76qsdqqj3-source/pkgs/development/libraries/openssl/default.nix";
  line = 303;
}

Lets create just the meta.description for demonstration purposes.

Adding the meta attribute

Since we don't have a meta attribute this points to a default value that's incorrect.

Let's add the meta attribute and try it again:

# default.nix
{
  runCommand,
  coreutils,
}:
runCommand "testPackage" {
  nativeBuildInputs = [
    coreutils
  ];

  meta = {
    description = "test package";
};
} ''

  echo 'This is a Test' > $out
''
nix-instantiate --eval -A testPackage.meta.position
"/home/jr/src/nixpkgs/pkgs/misc/testPackage/default.nix:11"

Now it points us to the 11'th line, right where our meta.description is.

Let's stage our package so nix recognises it:

cd ~/nixpkgs
git add pkgs/misc/testPackage/
nix edit .#testPackage

The default.nix that we've been working on should open in your $EDITOR