name: nix-package description: Creating and debugging Nix packages - fetchers, hash generation, overlays, AppImage wrapping, and common build patterns for NixOS dotfiles. license: MIT
Nix Package Creation & Debugging
Guide for creating custom Nix packages, debugging build failures, and integrating packages into a NixOS overlay.
When to Use This Skill
- Creating a new package in
pkgs/ - Updating a package version (hash mismatch)
- Debugging eval errors or build failures
- Wrapping an AppImage or prebuilt binary
- Adding a package to the overlay
Rapid Prototyping
Before writing a full package, test dependencies or binaries in a shell:
# Enter shell with specific packages
nix-shell -p openssl pkg-config gnumake
# Test a prebuilt binary's dynamic links
nix-shell -p ldd --run "ldd ./my-binary"
Boilerplate Generation
Use nix-init to automatically generate a package from a URL or repo:
nix-shell -p nix-init --run "nix-init https://github.com/user/repo"
It handles hash generation, fetchers, and standard build inputs automatically.
Adding a New Package
Step 1: Create pkgs/<name>/default.nix (or pkgs/<name>.nix)
URL/Tarball (prebuilt binary)
{lib, stdenv, fetchurl, ...}: let
pname = "my-app";
version = "1.2.3";
in stdenv.mkDerivation {
inherit pname version;
src = fetchurl {
url = "https://example.com/my-app-${version}.tar.gz";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
installPhase = ''
mkdir -p $out/bin
cp my-app $out/bin/
chmod +x $out/bin/my-app
'';
meta = {
description = "My application";
homepage = "https://example.com";
license = lib.licenses.mit;
platforms = lib.platforms.linux;
mainProgram = "my-app";
};
}
AppImage
{lib, appimageTools, fetchurl}: let
pname = "my-app";
version = "1.2.3";
src = fetchurl {
url = "https://example.com/my-app-${version}.AppImage";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
in appimageTools.wrapType2 {
inherit pname version src;
meta = {
description = "My application";
homepage = "https://example.com";
license = lib.licenses.unfree;
platforms = ["x86_64-linux"];
mainProgram = pname;
};
}
GitHub Source
{lib, stdenv, fetchFromGitHub, cmake, ...}: stdenv.mkDerivation rec {
pname = "my-tool";
version = "1.2.3";
src = fetchFromGitHub {
owner = "org";
repo = "repo";
rev = "v${version}";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
nativeBuildInputs = [cmake];
meta = {
description = "My tool";
homepage = "https://github.com/org/repo";
license = lib.licenses.mit;
platforms = lib.platforms.linux;
};
}
Step 2: Register in overlay (modules/overlays/pkgs.nix)
(_self: super: {
my-app = super.callPackage ../../pkgs/my-app.nix {};
# For subdirectory packages:
my-app = super.callPackage ../../pkgs/my-app {};
})
Step 3: Add to host config
home.packages = with pkgs; [my-app];
# or system-wide:
environment.systemPackages = with pkgs; [my-app];
Updating Existing Packages
Before editing a package for a new version, check what changed upstream:
# For electron apps — fetch package.json from the new tag to verify:
# - electron version (may need electron_NN bump in nix)
# - dropped dependencies (e.g. sass-embedded removed → delete preBuild hook)
curl -s "https://raw.githubusercontent.com/<owner>/<repo>/refs/tags/v<version>/package.json" | jq '{electron: .devDependencies.electron}'
Changes to watch for:
- Electron version bump → update
electron_NNinput - Dropped build-time deps (sass, native addons) → remove corresponding
nativeBuildInputsand build hooks - New postPatch substitutions → check if old
substituteInPlacepaths still exist in the new source
Getting Hashes
Start with a fake hash — Nix will tell you the real one:
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
Build fails with: hash mismatch ... got: sha256-<actual> — copy that value.
# Prefetch before writing the package
nix-prefetch-url --type sha256 <url>
# Convert hex → SRI: nix hash to-sri --type sha256 <hex>
# For GitHub
nix-prefetch-fetchFromGitHub --owner <org> --repo <repo> --rev <tag>
Debugging Build Failures
Eval errors (before build)
sudo nixos-rebuild build --flake .#framework --show-trace
# Cached failure? Force re-eval:
sudo nixos-rebuild build --flake .#framework --option eval-cache false
Common eval errors:
attribute 'X' missing→ Check overlay registration or callPackage argsinfinite recursion→ Usesupernotselfin overlaycannot coerce X to string→ Wrong type passed to a string context
Build errors (during build)
# Build single package in isolation
# NOTE: nix build -f pkgs/<name>.nix only works for argument-free packages.
# For callPackage-style packages (with lib, stdenv, etc.) use:
nix build --impure --expr 'let pkgs = import <nixpkgs> {}; in pkgs.callPackage ./pkgs/<name>.nix {}'
# Interactive build env
nix develop .#<derivation>
Common build errors:
- Missing library → add to
buildInputs - Missing build tool → add to
nativeBuildInputs - Prebuilt binary RPATH issues:
nativeBuildInputs = [patchelf autoPatchelfHook]; buildInputs = [stdenv.cc.cc.lib zlib];
Using Unstable Packages
# In a module
{pkgs-unstable, ...}: {
home.packages = [pkgs-unstable.some-package];
}
Checklist for New Package
-
nix build -f pkgs/<name>.nixsucceeds - Overlay entry in
modules/overlays/pkgs.nix - Added to
home.packagesorenvironment.systemPackages -
sudo nixos-rebuild build --flake .#frameworksucceeds -
meta.mainProgramset for executables - License set (
lib.licenses.unfreefor proprietary)