Dotfiles-nix nix-package
Creating and debugging Nix packages - fetchers, hash generation, overlays, AppImage wrapping, and common build patterns for NixOS dotfiles.
install
source · Clone the upstream repo
git clone https://github.com/not-matthias/dotfiles-nix
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/not-matthias/dotfiles-nix "$T" && mkdir -p ~/.claude/skills && cp -r "$T/modules/home/programs/cli-agents/shared/skills/nix-package" ~/.claude/skills/not-matthias-dotfiles-nix-nix-package && rm -rf "$T"
manifest:
modules/home/programs/cli-agents/shared/skills/nix-package/SKILL.mdsource content
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
to automatically generate a package from a URL or repo:nix-init
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
)
pkgs/<name>/default.nixpkgs/<name>.nixURL/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
)
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
inputelectron_NN - Dropped build-time deps (sass, native addons) → remove corresponding
and build hooksnativeBuildInputs - New postPatch substitutions → check if old
paths still exist in the new sourcesubstituteInPlace
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:
→ Check overlay registration or callPackage argsattribute 'X' missing
→ Useinfinite recursion
notsuper
in overlayself
→ Wrong type passed to a string contextcannot coerce X to string
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
-
succeedsnix build -f pkgs/<name>.nix - Overlay entry in
modules/overlays/pkgs.nix - Added to
orhome.packagesenvironment.systemPackages -
succeedssudo nixos-rebuild build --flake .#framework -
set for executablesmeta.mainProgram - License set (
for proprietary)lib.licenses.unfree