Package Workflow
Create, maintain, and debug Nix packages.
When to Use
- “create nix package”
- “package application with nix”
- “write nix derivation”
- “buildGoModule”
Quick Commands
Building Packages
# Build package
nix build .#package-name
# Test package
nix build .#package-name -L
# Run package
nix run .#package-name
# Install package
nix profile install .#package-name
Testing Packages
# Build and test
nix build .#package-name --rebuild
# Check meta attributes
nix eval .#package-name.meta --json
# Check outputs
nix eval .#package-name.outputs
Package Structure
Basic Package Template
# pkgs/mypackage/default.nix
{ lib
, stdenv
, fetchFromGitHub
}:
stdenv.mkDerivation rec {
pname = "mypackage";
version = "1.0.0";
src = fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
meta = with lib; {
description = "Short package description";
homepage = "https://github.com/owner/repo";
license = licenses.mit;
maintainers = with maintainers; [ ];
platforms = platforms.all;
};
}
Using callPackage
# pkgs/default.nix
{ pkgs }:
{
mypackage = pkgs.callPackage ./mypackage { };
anotherpkg = pkgs.callPackage ./anotherpkg { };
}
Language-Specific Packaging
Go Packages
{ lib
, buildGoModule
, fetchFromGitHub
}:
buildGoModule rec {
pname = "mygoapp";
version = "1.0.0";
src = fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}";
hash = "sha256-...";
};
vendorHash = "sha256-...";
# Or if no vendor directory:
# vendorHash = null;
# Flags passed to go build
ldflags = [
"-s"
"-w"
"-X main.version=${version}"
];
# Subdirectory with main package
subPackages = [ "cmd/myapp" ];
meta = with lib; {
description = "My Go application";
homepage = "https://github.com/owner/repo";
license = licenses.mit;
maintainers = with maintainers; [ ];
};
}
Getting vendorHash
# Method 1: Use fake hash
# Set: vendorHash = lib.fakeSha256;
# Build will fail with correct hash
# Method 2: Use nix-prefetch
nix-prefetch -f '<nixpkgs>' '{
mygoapp = (import ./. {}).mygoapp.go-modules;
}'
# Method 3: Build and copy error
nix build .#mygoapp 2>&1 | grep "got:"
Rust Packages
{ lib
, rustPlatform
, fetchFromGitHub
, pkg-config
, openssl
}:
rustPlatform.buildRustPackage rec {
pname = "myrustapp";
version = "1.0.0";
src = fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}";
hash = "sha256-...";
};
cargoHash = "sha256-...";
nativeBuildInputs = [ pkg-config ];
buildInputs = [ openssl ];
meta = with lib; {
description = "My Rust application";
homepage = "https://github.com/owner/repo";
license = licenses.mit;
maintainers = with maintainers; [ ];
};
}
Python Packages
{ lib
, python3Packages
, fetchPypi
}:
python3Packages.buildPythonPackage rec {
pname = "mypythonpkg";
version = "1.0.0";
src = fetchPypi {
inherit pname version;
hash = "sha256-...";
};
propagatedBuildInputs = with python3Packages; [
requests
flask
];
checkInputs = with python3Packages; [
pytest
];
pythonImportsCheck = [ "mypythonpkg" ];
meta = with lib; {
description = "My Python package";
homepage = "https://github.com/owner/repo";
license = licenses.mit;
maintainers = with maintainers; [ ];
};
}
Node.js Packages
{ lib
, buildNpmPackage
, fetchFromGitHub
}:
buildNpmPackage rec {
pname = "mynodeapp";
version = "1.0.0";
src = fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}";
hash = "sha256-...";
};
npmDepsHash = "sha256-...";
# Skip npm install phase if needed
dontNpmBuild = true;
meta = with lib; {
description = "My Node.js application";
homepage = "https://github.com/owner/repo";
license = licenses.mit;
maintainers = with maintainers; [ ];
};
}
Source Fetchers
fetchFromGitHub
src = fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}"; # Or specific commit
hash = "sha256-...";
};
# Get hash with:
nix-prefetch-github owner repo --rev v1.0.0
fetchurl
src = fetchurl {
url = "https://example.com/file-${version}.tar.gz";
hash = "sha256-...";
};
# Get hash with:
nix-prefetch-url https://example.com/file.tar.gz
fetchgit
src = fetchgit {
url = "https://git.example.com/repo.git";
rev = "commit-hash";
hash = "sha256-...";
};
fetchzip
src = fetchzip {
url = "https://example.com/archive.zip";
hash = "sha256-...";
stripRoot = false; # If archive doesn't have single root directory
};
Build Phases
Standard Phases
stdenv.mkDerivation {
# ...
# Phases run in order:
# unpackPhase - Extract source
# patchPhase - Apply patches
# configurePhase - Run ./configure
# buildPhase - Run make
# checkPhase - Run tests
# installPhase - Install files
# fixupPhase - Fix up outputs
# Override phases
buildPhase = ''
make all
'';
installPhase = ''
mkdir -p $out/bin
cp myapp $out/bin/
'';
}
Custom Phases
stdenv.mkDerivation {
# ...
preBuild = ''
echo "Before build"
'';
postInstall = ''
echo "After install"
# Wrap binary with runtime dependencies
wrapProgram $out/bin/myapp \
--prefix PATH : ${lib.makeBinPath [ dependency ]}
'';
}
Skip Phases
stdenv.mkDerivation {
# ...
# Skip configure phase
dontConfigure = true;
# Skip build phase
dontBuild = true;
# Skip tests
doCheck = false;
}
Dependencies
Types of Dependencies
stdenv.mkDerivation {
# Build-time dependencies (native to build platform)
nativeBuildInputs = [
pkg-config
cmake
makeWrapper
];
# Runtime dependencies (target platform)
buildInputs = [
openssl
zlib
];
# Dependencies for running tests
checkInputs = [
pytest
];
# Propagated to things that depend on this
propagatedBuildInputs = [
libxml2
];
}
Finding Dependencies
# Search for package
nix search nixpkgs openssl
# Check package contents
nix-locate bin/openssl
# Find library
nix-locate --top-level lib/libssl.so
Multiple Outputs
Define Outputs
stdenv.mkDerivation {
pname = "myapp";
version = "1.0.0";
outputs = [ "out" "dev" "doc" ];
installPhase = ''
# Main output
mkdir -p $out/bin
cp myapp $out/bin/
# Development files
mkdir -p $dev/include
cp *.h $dev/include/
# Documentation
mkdir -p $doc/share/doc
cp README.md $doc/share/doc/
'';
}
Use Specific Output
# Build specific output
nix build .#package.dev
# Install specific output
nix profile install .#package.doc
Overriding Packages
Override Attributes
# Override specific attributes
mypackage = pkgs.mypackage.overrideAttrs (old: {
version = "2.0.0";
src = fetchFromGitHub {
# new source
};
});
Override Arguments
# Override function arguments
mypackage = pkgs.mypackage.override {
enableFeature = true;
};
Add Patches
mypackage = pkgs.mypackage.overrideAttrs (old: {
patches = (old.patches or []) ++ [
./fix-bug.patch
];
});
Meta Attributes
Complete Meta Example
meta = with lib; {
description = "Short one-line description";
longDescription = ''
Longer multi-line description
explaining the package.
'';
homepage = "https://example.com";
changelog = "https://example.com/changelog";
license = licenses.mit;
# Or multiple licenses
# license = with licenses; [ mit asl20 ];
maintainers = with maintainers; [ username ];
platforms = platforms.linux;
# Or specific platforms
# platforms = [ "x86_64-linux" "aarch64-linux" ];
broken = false;
# Mark as unfree if needed
# unfree = true;
};
Common Licenses
licenses.mit
licenses.gpl3
licenses.gpl3Only
licenses.gpl3Plus
licenses.asl20
licenses.bsd3
licenses.mpl20
licenses.unfree
Testing Packages
Build and Test
# Build package
nix build .#mypackage -L
# Run tests
nix build .#mypackage --rebuild
# Check specific test
nix build .#mypackage.tests.sometest
Test Installation
# Install to temporary profile
nix profile install .#mypackage --profile /tmp/test-profile
# Test binary
/tmp/test-profile/bin/myapp --version
# Clean up
rm -rf /tmp/test-profile
Check Dependencies
# List runtime dependencies
nix-store -q --references $(nix build .#mypackage --no-link --print-out-paths)
# List all dependencies (recursive)
nix-store -q --requisites $(nix build .#mypackage --no-link --print-out-paths)
# Check for unwanted dependencies
nix-store -q --tree result | grep unwanted
Debugging Packages
Build Failures
# Keep build directory on failure
nix build .#mypackage --keep-failed
# Inspect failed build
cd /tmp/nix-build-mypackage-*.drv-0/
ls -la
cat build.log
Enter Build Environment
# Enter environment
nix develop .#mypackage
# Run build phases manually
unpackPhase
cd $sourceRoot
configurePhase
buildPhase
Check Build Script
# Show derivation
nix show-derivation .#mypackage
# Show builder
nix show-derivation .#mypackage | jq '.[].env.builder'
# Show build args
nix show-derivation .#mypackage | jq '.[].args'
Package Best Practices
- Pin versions explicitly: Use specific version numbers
- Add meta attributes: Description, homepage, license, platforms
- Use appropriate fetcher: fetchFromGitHub, fetchurl, etc.
- Include tests: Enable doCheck when tests exist
- Multiple outputs: Separate dev files, docs, etc.
- Minimal dependencies: Only include what’s needed
- Wrap binaries properly: Use makeWrapper for runtime deps
- Document patches: Comment why patches are needed
- Test cross-platform: Build for different platforms
- Follow nixpkgs conventions: Look at similar packages
Common Patterns
Wrapper Script
{ lib, stdenv, makeWrapper, myapp, dependency }:
stdenv.mkDerivation {
pname = "myapp-wrapped";
version = myapp.version;
nativeBuildInputs = [ makeWrapper ];
buildCommand = ''
makeWrapper ${myapp}/bin/myapp $out/bin/myapp \
--prefix PATH : ${lib.makeBinPath [ dependency ]}
'';
}
Conditional Features
{ lib
, stdenv
, enableFeature ? false
, optionalDependency
}:
stdenv.mkDerivation {
pname = "myapp";
buildInputs = [ ]
++ lib.optional enableFeature optionalDependency;
configureFlags = [ ]
++ lib.optional enableFeature "--enable-feature";
}
Platform-Specific
{ lib, stdenv, darwin }:
stdenv.mkDerivation {
pname = "myapp";
buildInputs = [ ]
++ lib.optionals stdenv.isLinux [ linuxDep ]
++ lib.optionals stdenv.isDarwin [
darwin.apple_sdk.frameworks.Security
];
}