Jump to content

Nixpkgs/Patching Nixpkgs: Difference between revisions

From Official NixOS Wiki
N0099 (talk | contribs)
replace importing nixpkgs: https://discourse.nixos.org/t/1000-instances-of-nixpkgs/17347 with existing NixOS module param `pkgs`, and replace fetchurl with fetchpatch2 that will do normalization on generated patch
Perchun (talk | contribs)
Add multiple other options to choose from
Line 1: Line 1:
Sometimes it may be required to patch a copy of Nixpkgs directly, rather than use an overlay to patch an individual package. One scenario of where this might happen is if Nixpkgs doesn't contain a change you need, but you find some
Sometimes it may be required to patch a copy of nixpkgs directly, rather than use an overlay to patch an individual package. One scenario of where this might happen is if nixpkgs doesn't contain a change you need, but you find some
existing PR that has yet to be merged, and so want to leverage those changes prior to them being merged.
existing PR that has yet to be merged, and so want to leverage those changes prior to them being merged.


For the sake of example, let's say you are using the software package Ghidra, and the latest version available on
There are many ways to patch nixpkgs, each with its own advantages and disadvantages. We will go through the most popular ones:
Nixpkgs unstable is [https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=ghidra Ghidra 11.1.2].
{| class="wikitable" style="text-align: center; width: 500px;"
You know that the Ghidra developers have recently released 11.2, and want to use it before it's in Nixpkgs. You could try to create an overlay and manually update the package, but maybe in the process you realize it is not a
|-
trivial update. You may find there is already a pending PR to Nixpkgs with the updated version and
! Name            !! Method          !! IFD !! Requires Flakes
changes you want, such as [https://github.com/NixOS/nixpkgs/pull/344917 this] PR introducing Ghidra 11.2. If you didn't
|-
want to wait for this PR to be merged into Nixpkgs, you could apply the PR patch directly to your Nixpkgs instead. Which we will do below.
| applyPatch      || .patch files    ||  X  ||
|-
| nixpkgs-patcher || .patch files    ||  X  ||  X
|-
| nix-patcher    || .patch files    ||    ||  X
|-
| gh-cherry-pick  || git cherry-pick ||    ||
|}


There are of course other reasons you may wish to use a patched Nixpkgs and the method applies to any of those cases.
Most of the solutions work on the <code>.patch</code> files, but those have a huge disadvantage - they quickly go out of date and have to be constantly rebased. This is especially true for solutions that use Git to apply patches. Another disadvantage may be [https://nixos.org/manual/nix/unstable/language/import-from-derivation IFD], which may result in [https://nixcademy.com/posts/what-is-ifd-ups-and-downs/ performance and runtime issues].


== Usage ==


First you will need to apply the Nixpkgs PR patch to some copy of Nixpkgs. In this example we will assume a NixOS modules has parameter <code>pkgs</code> pointing to <code>nixos-unstable</code>.
For the sake of example, let's say you are using the software package [https://ghidra-sre.org/ Ghidra], and the latest version available on
We will use the [https://noogle.dev/f/pkgs/applyPatches <code>applyPatches</code>] function to apply the unmerged PR changes to
nixpkgs unstable is [https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=ghidra 11.1.2].
<code>nixpkgs-unstable</code>. First we need to covert the PR to a patch, which can be done by appending
You know that the Ghidra developers have recently released 11.2, and want to use it before it's in nixpkgs. You could try to create an overlay and manually update the package, but maybe in the process, you realize it is not a
<code>.patch</code> to the github PR
trivial update. You may find there is already a pending PR to nixpkgs with the updated version and
url. This will redirect to another link, for example https://github.com/NixOS/nixpkgs/pull/344917.patch will
changes you want, such as [https://github.com/NixOS/nixpkgs/pull/344917 this PR]. If you didn't
redirect to https://patch-diff.githubusercontent.com/raw/NixOS/nixpkgs/pull/344917.patch. Then we can use <code>[https://nixos.org/manual/nixpkgs/stable/#fetchpatch fetchpatch]</code> or <code>[https://noogle.dev/f/pkgs/fetchpatch2 fetchpatch2]</code> to normalize this patch that generated by GitHub:
want to wait for that PR to be merged into nixpkgs, you could apply the PR patch directly to your nixpkgs instead. There are, of course, other reasons you may wish to use a patched nixpkgs, and the method applies to any of those cases.
 
To get your patch, you will usually need to find a list of commits from the desired PR and fetch them. Try to avoid fetching something like https://github.com/NixOS/nixpkgs/pull/344917.diff, as it can change if the PR is still open. If you are fetching from GitHub, don't forget to add <code>?full_index=1</code> at the end of the URL for better reproducibility.
 
<syntaxhighlight lang="nix">
# for https://github.com/NixOS/nixpkgs/pull/344917
pkgs.fetchpatch2 {
  # note the `.diff` at the end!
  url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
  hash = "sha256-/XnEHRQahmHUNstgTVkQC+LGp7Z0wmf+qPVyKntgZG0=";
}
</syntaxhighlight>
 
Or you can also fetch a range, if you want to fetch multiple commits in one go


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{ pkgs, ... }:
pkgs.fetchpatch2 {
  # 4c030cf is a parent of the first PR commit
  url = "https://github.com/NixOS/nixpkgs/compare/4c030cf309bffa9cd87336705e96ce941ce977d9...05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
  hash = "sha256-gH8NHJ+i1snTN/ehFopQko8BfeO0PxQaFRCQCoLzVfg=";
}
</syntaxhighlight>
 
=== Using <code>[https://github.com/NixOS/nixpkgs/blob/f6e07eb0e1e42c38f8e0b6ffc221b7bf73bece4b/pkgs/build-support/trivial-builders/default.nix#L981 applyPatches]</code> function ===
 
<code>applyPatches</code> is a nixpkgs function that applies a list of patches to a source directory. Internally, it just creates a new derivation via <code>stdenv.mkDerivation</code> and passes all of the patches to the usual <code>[https://github.com/NixOS/nixpkgs/blob/12c894cb74171bcec130756cc0f77720fca6587d/pkgs/stdenv/generic/setup.sh#L1371-L1404 patchPhase]</code>.


<syntaxhighlight lang="nix">
let
let
  # to access the `applyPatches` function, you will need
  # to import unpatched nixpkgs first
  pkgs = import <nixpkgs> {
    # with flakes, you may also need to hardcode the system
    # system = "x86_64-linux";
  };
   nixpkgs-344917-drv =
   nixpkgs-344917-drv =
     pkgs.applyPatches {
     pkgs.applyPatches {
Line 28: Line 65:
       patches = [
       patches = [
         (pkgs.fetchpatch2 {
         (pkgs.fetchpatch2 {
           url = "https://github.com/NixOS/nixpkgs/pull/344917.patch";
           url = "https://github.com/NixOS/nixpkgs/compare/4c030cf309bffa9cd87336705e96ce941ce977d9...05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
           sha256 = "sha256-aws9J5ZNUyz4Z2RqPVEovBTNng4AdhzS03Bqg8jejWQ=";
           hash = "sha256-gH8NHJ+i1snTN/ehFopQko8BfeO0PxQaFRCQCoLzVfg=";
         })
         })
       ];
       ];
Line 35: Line 72:
   nixpkgs-344917 = import nixpkgs-344917-drv { inherit (pkgs.stdenv) system; };
   nixpkgs-344917 = import nixpkgs-344917-drv { inherit (pkgs.stdenv) system; };
in
in
# ...
nixpkgs-344917.ghidra.version # == 11.2 :tada:
</syntaxhighlight>
 
In the above example, we create a derivation with the patch applied, called <code>nixpkgs-344917-drv</code>. Pay attention, we take the <code>src</code> attribute to get back the original (unpatched) nixpkgs. We then import that new derivation, which we assign to <code>nixpkgs-344917</code>. Now we can use <code>nixpkgs-344917</code> to access the Ghidra 11.2 package, as well as any other packages normally available in nixpkgs.
 
=== Using [https://github.com/gepbird/nixpkgs-patcher nixpkgs-patcher] ===
 
nixpkgs-patcher is a nice wrapper around <code>[https://wiki.nixos.org/wiki/Nixpkgs/Patching_Nixpkgs#Using_applyPatches_function applyPatches]</code> which makes integration with NixOS flake's system definition very easy:
 
<syntaxhighlight lang="nix">
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    nixpkgs-patcher.url = "github:gepbird/nixpkgs-patcher";
 
    nixpkgs-patch-ghidra-11-2 = {
      url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
      flake = false;
    };
    nixpkgs-patch-ghidra-11-2-ret-sync = {
      url = "https://github.com/NixOS/nixpkgs/commit/05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
      flake = false;
    };
  };
 
  outputs =
    { nixpkgs-patcher, ... }@inputs:
    {
      nixosConfigurations.yourHostname = nixpkgs-patcher.lib.nixosSystem {
        modules = [
          ./configuration.nix
          ./hardware-configuration.nix
        ];
        specialArgs = inputs;
      };
    };
}
</syntaxhighlight>
 
=== Using [https://github.com/katrinafyi/nix-patcher nix-patcher] ===
 
nix-patcher is a script that automatically gets a list of patches from your <code>flake.nix</code> and applies them to your nixpkgs fork sequentially.
 
<syntaxhighlight lang="nix"># flake.nix
{
  inputs = {
    nixpkgs = "github:MyName/nixpkgs/patched-branch";
    nixpkgs-upstream = "github:NixOS/nixpkgs/nixos-unstable";
 
    nixpkgs-patch-10 = {
      url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
      flake = false;
    };
    nixpkgs-patch-20 = {
      url = "https://github.com/NixOS/nixpkgs/commit/05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
      flake = false;
    };
  };
}</syntaxhighlight>
 
nix-patcher will notice the special "-upstream" and "-patch-" suffixes and match these with "nixpkgs-upstream". When run, the repo and branch to update will be taken from the "nixpkgs" inputs. The upstream, patches, and fork inputs are linked together by their common prefix (here, "nixpkgs").
 
=== Using [https://github.com/PerchunPak/gh-cherry-pick gh-cherry-pick] ===
 
gh-cherry-pick interacts directly with the GitHub API to cherry-pick or merge branches. This tool is unique in that it doesn't depend on or use Nix; it is a generic script that fits perfectly for our use case.
 
<syntaxhighlight lang="bash">
# This tool requires a classic token with `repo` and `workflow` permissions
# or a fine-grained token with `contents` and `workflows` permissions
GITHUB_TOKEN=ghp_... gh-cherry-pick \
  --target MyOrg/nixpkgs@patched \
  `: # cherry-pick these commits` \
  NixOS/nixpkgs/3f5ba52cc4701bf341457dfe5f6cb58e0cbb7f83 \
  NixOS/nixpkgs/49ba75edefc8dc4fee45482f77a280ddd7121797 \
  `: # or merge the entire branch!` \
  Someone/nixpkgs@pr-branch
</syntaxhighlight>
</syntaxhighlight>


In the above example we create a derivation with the patch applied, called <code>nixpkgs-344917-drv</code>. Pay attention we take the <code>src</code> attribute to get back the original (unpatched) nixpkgs. We then import that new derivation which we assign to <code>nixpkgs-344917</code>. Now we can use <code>nixpkgs-344917</code> to access the
Very similar to [https://wiki.nixos.org/wiki/Nixpkgs/Patching_Nixpkgs#Using_nix-patcher nix-patcher], it maintains a fork of nixpkgs and applies the patches on it sequentially. The main difference is that it doesn't operate on patch files, but runs <code>[https://git-scm.com/docs/git-cherry-pick git cherry-pick]</code> directly through GitHub API. This is a huge advantage, since Git can automatically rebase commits instead of forcing you to do that.
Ghidra 11.2 package, as well as any other packages normally available in nixpkgs.


== Resources ==
== Resources ==
Line 46: Line 157:


* [https://ertt.ca/nix/patch-nixpkgs/ Patching <nixpkgs>]
* [https://ertt.ca/nix/patch-nixpkgs/ Patching <nixpkgs>]
* [https://discourse.nixos.org/t/patch-nixpkgs-using-cherry-picks-without-local-clone/76925 gh-cherry-pick announcement (and discussion about other tools)]

Revision as of 18:31, 13 April 2026

Sometimes it may be required to patch a copy of nixpkgs directly, rather than use an overlay to patch an individual package. One scenario of where this might happen is if nixpkgs doesn't contain a change you need, but you find some existing PR that has yet to be merged, and so want to leverage those changes prior to them being merged.

There are many ways to patch nixpkgs, each with its own advantages and disadvantages. We will go through the most popular ones:

Name Method IFD Requires Flakes
applyPatch .patch files X
nixpkgs-patcher .patch files X X
nix-patcher .patch files X
gh-cherry-pick git cherry-pick

Most of the solutions work on the .patch files, but those have a huge disadvantage - they quickly go out of date and have to be constantly rebased. This is especially true for solutions that use Git to apply patches. Another disadvantage may be IFD, which may result in performance and runtime issues.

Usage

For the sake of example, let's say you are using the software package Ghidra, and the latest version available on nixpkgs unstable is 11.1.2. You know that the Ghidra developers have recently released 11.2, and want to use it before it's in nixpkgs. You could try to create an overlay and manually update the package, but maybe in the process, you realize it is not a trivial update. You may find there is already a pending PR to nixpkgs with the updated version and changes you want, such as this PR. If you didn't want to wait for that PR to be merged into nixpkgs, you could apply the PR patch directly to your nixpkgs instead. There are, of course, other reasons you may wish to use a patched nixpkgs, and the method applies to any of those cases.

To get your patch, you will usually need to find a list of commits from the desired PR and fetch them. Try to avoid fetching something like https://github.com/NixOS/nixpkgs/pull/344917.diff, as it can change if the PR is still open. If you are fetching from GitHub, don't forget to add ?full_index=1 at the end of the URL for better reproducibility.

# for https://github.com/NixOS/nixpkgs/pull/344917
pkgs.fetchpatch2 {
  # note the `.diff` at the end!
  url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
  hash = "sha256-/XnEHRQahmHUNstgTVkQC+LGp7Z0wmf+qPVyKntgZG0=";
}

Or you can also fetch a range, if you want to fetch multiple commits in one go

pkgs.fetchpatch2 {
  # 4c030cf is a parent of the first PR commit
  url = "https://github.com/NixOS/nixpkgs/compare/4c030cf309bffa9cd87336705e96ce941ce977d9...05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
  hash = "sha256-gH8NHJ+i1snTN/ehFopQko8BfeO0PxQaFRCQCoLzVfg=";
}

Using applyPatches function

applyPatches is a nixpkgs function that applies a list of patches to a source directory. Internally, it just creates a new derivation via stdenv.mkDerivation and passes all of the patches to the usual patchPhase.

let
  # to access the `applyPatches` function, you will need
  # to import unpatched nixpkgs first
  pkgs = import <nixpkgs> {
    # with flakes, you may also need to hardcode the system
    # system = "x86_64-linux";
  };
  nixpkgs-344917-drv =
    pkgs.applyPatches {
      src = pkgs.path;
      patches = [
        (pkgs.fetchpatch2 {
          url = "https://github.com/NixOS/nixpkgs/compare/4c030cf309bffa9cd87336705e96ce941ce977d9...05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
          hash = "sha256-gH8NHJ+i1snTN/ehFopQko8BfeO0PxQaFRCQCoLzVfg=";
        })
      ];
    };
  nixpkgs-344917 = import nixpkgs-344917-drv { inherit (pkgs.stdenv) system; };
in
nixpkgs-344917.ghidra.version # == 11.2 :tada:

In the above example, we create a derivation with the patch applied, called nixpkgs-344917-drv. Pay attention, we take the src attribute to get back the original (unpatched) nixpkgs. We then import that new derivation, which we assign to nixpkgs-344917. Now we can use nixpkgs-344917 to access the Ghidra 11.2 package, as well as any other packages normally available in nixpkgs.

nixpkgs-patcher is a nice wrapper around applyPatches which makes integration with NixOS flake's system definition very easy:

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

    nixpkgs-patch-ghidra-11-2 = {
      url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
      flake = false;
    };
    nixpkgs-patch-ghidra-11-2-ret-sync = {
      url = "https://github.com/NixOS/nixpkgs/commit/05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
      flake = false;
    };
  };

  outputs =
    { nixpkgs-patcher, ... }@inputs:
    {
      nixosConfigurations.yourHostname = nixpkgs-patcher.lib.nixosSystem {
        modules = [
          ./configuration.nix
          ./hardware-configuration.nix
        ];
        specialArgs = inputs;
      };
    };
}

nix-patcher is a script that automatically gets a list of patches from your flake.nix and applies them to your nixpkgs fork sequentially.

# flake.nix
{
  inputs = {
    nixpkgs = "github:MyName/nixpkgs/patched-branch";
    nixpkgs-upstream = "github:NixOS/nixpkgs/nixos-unstable";

    nixpkgs-patch-10 = {
      url = "https://github.com/NixOS/nixpkgs/commit/55367b381af23045c93f7b85171db850e628ef7d.diff?full_index=1";
      flake = false;
    };
    nixpkgs-patch-20 = {
      url = "https://github.com/NixOS/nixpkgs/commit/05071d58a87c832f6366fff6c8ec328cb28a2a66.diff?full_index=1";
      flake = false;
    };
  };
}

nix-patcher will notice the special "-upstream" and "-patch-" suffixes and match these with "nixpkgs-upstream". When run, the repo and branch to update will be taken from the "nixpkgs" inputs. The upstream, patches, and fork inputs are linked together by their common prefix (here, "nixpkgs").

gh-cherry-pick interacts directly with the GitHub API to cherry-pick or merge branches. This tool is unique in that it doesn't depend on or use Nix; it is a generic script that fits perfectly for our use case.

# This tool requires a classic token with `repo` and `workflow` permissions
# or a fine-grained token with `contents` and `workflows` permissions
GITHUB_TOKEN=ghp_... gh-cherry-pick \
  --target MyOrg/nixpkgs@patched \
  `: # cherry-pick these commits` \
  NixOS/nixpkgs/3f5ba52cc4701bf341457dfe5f6cb58e0cbb7f83 \
  NixOS/nixpkgs/49ba75edefc8dc4fee45482f77a280ddd7121797 \
  `: # or merge the entire branch!` \
  Someone/nixpkgs@pr-branch

Very similar to nix-patcher, it maintains a fork of nixpkgs and applies the patches on it sequentially. The main difference is that it doesn't operate on patch files, but runs git cherry-pick directly through GitHub API. This is a huge advantage, since Git can automatically rebase commits instead of forcing you to do that.

Resources

The following are resources that go into more depth on this topic.