Best practices for packaging pre-compiled binaries (.deb, .rpm, .tar.gz, AppImage) for NixOS, handling library dependencies, or facing "library not found" errors with binary distributions
Resources
2Install
npx skillscat add lihaoze123/my-claude-code/nix-packaging-best-practices Install via the SkillsCat registry.
SKILL.md
Packaging Binary Distributions for Nix
Extract and patch binary packages within Nix builds for reproducibility.
Core Principle
Source from original archive directly, never from pre-extracted directories.
When to Use
- Converting binary packages (.deb, .rpm, .tar.gz, .zip) to Nix derivations
- Packaging proprietary/closed-source software distributed as binaries
- Electron/GUI apps show "library not found" errors
- User provides pre-extracted binary contents
- Binary distributions need library path fixes
Don't use for:
- Software available in nixpkgs
- Source-based packages (use standard derivation)
- AppImages (use appimage-run or extract and patch)
Quick Reference
| Topic | Rule File |
|---|---|
| Core pattern for .deb packages | essential-pattern |
| .rpm, .tar.gz, .zip extraction | archive-formats |
| nativeBuildInputs vs buildInputs | dependencies |
| Local vs remote source files | source-files |
| Common pitfalls and fixes | common-mistakes |
| Finding missing libraries with ldd | finding-libraries |
| Quick testing with steam-run | quick-testing |
| Troubleshooting autoPatchelf errors | troubleshooting-autoPatchelf |
| Version variable in filename | version-management |
| Electron app dependencies | electron-apps |
| FHS env for resistant binaries | advanced-fhs-env |
| Build phases and hooks | build-phases |
| Wrapper scripts with makeWrapper | wrapper-programs |
Essential Pattern (.deb)
{ pkgs }:
pkgs.stdenv.mkDerivation rec {
pname = "appname";
version = "1.0.0";
src = ./AppName-${version}-linux-amd64.deb;
nativeBuildInputs = with pkgs; [
autoPatchelfHook # Fixes library paths
dpkg # Extracts .deb
];
buildInputs = with pkgs; [
stdenv.cc.cc.lib
glib
gtk3
];
unpackPhase = ''
ar x $src
tar xf data.tar.xz
'';
installPhase = ''
mkdir -p $out
cp -r opt/AppName/* $out/
'';
}Common Tasks
| Task | Solution |
|---|---|
| Local archive | src = ./package-${version}.tar.gz |
| Remote archive | src = fetchurl { url = "..."; hash = "sha256-..."; } |
| Extract .deb | ar x $src && tar xf data.tar.xz + dpkg |
| Extract .rpm | rpm2cpio $src | cpio -idmv + rpm, cpio |
| Extract .tar.gz | Auto-detected by stdenv |
| Extract .zip | Add unzip to nativeBuildInputs |
| Fix libraries | Add autoPatchelfHook to nativeBuildInputs |
| Find missing libs | Run ldd result/bin/app for "not found" |
| Quick test binary | nix-shell -p steam-run; steam-run ./binary |
| Debug autoPatchelf | nix log /nix/store/...-drv |
| Search libraries | nix-locate libX11.so.6 |
| Wrapper scripts | Add makeWrapper to nativeBuildInputs |
| Version sync | Use src = ./app-${version}.tar.gz |
Dependency Categories
nativeBuildInputs: Build-time tools (dpkg, autoPatchelfHook, makeWrapper)
buildInputs: Runtime libraries (gtk3, glib, libpulseaudio)
propagatedBuildInputs: Rarely needed for binary packaging
Common Library Mappings
| Missing Library | Nix Package |
|---|---|
libgtk-3.so.0 |
gtk3 |
libglib-2.0.so.0 |
glib |
libpulse.so.0 |
libpulseaudio |
libGL.so.1 |
mesa or libglvnd |
libxkbcommon.so.0 |
libxkbcommon (NOT xorg.libxkbcommon) |
libstdc++.so.6 |
stdenv.cc.cc.lib |
Red Flags - STOP
- "User already extracted it, use that directory" → NO, source from original archive
- "Absolute path works for me locally" → Breaks for others, use relative
- "Just add more libraries until it works" → Find actual dependencies with
ldd
How to Use
Read individual rule files for detailed explanations and code examples:
rules/essential-pattern.md
rules/archive-formats.md
rules/_sections.mdEach rule file contains:
- Brief explanation of why it matters
- Incorrect code example with explanation
- Correct code example with explanation
- Additional context and references
Full Compiled Document
For the complete guide with all rules expanded: AGENTS.md