Nix/NixOS package development, maintenance, and system configuration. Triggers: .nix, flake.nix, default.nix, nixpkgs, derivation, buildRustPackage, buildPythonPackage, NixOS modules. Covers package development, nixpkgs contributions, NixOS configuration, flakes, derivations, overlays, and troubleshooting build failures.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
Comprehensive guidelines for Nix package development, nixpkgs maintenance, NixOS configuration, and build troubleshooting.
| Task | Command | Notes |
|---|---|---|
| Build package | nix-build -A <attr> | Legacy builder |
| Build with flakes | nix build .#<package> | Modern flake-based |
| Develop environment | nix develop | Enter dev shell with dependencies |
| Run package | nix run .#<package> | Execute from flake |
| Show derivation | nix show-derivation | Inspect build inputs |
| Search packages | nix search nixpkgs <query> | Find packages |
| Update flake inputs | nix flake update | Update all inputs |
| Build NixOS config | nixos-rebuild build | Test config without activation |
| Check syntax | nix-instantiate --parse | Validate Nix syntax |
| Evaluate expression | nix-instantiate --eval | Check attribute values |
| Format code | nixfmt or alejandra | Format Nix files |
fetchFromGitHub, fetchurl, etc. in fetchers, not in build phasenix-hash or nix-prefetch-url to get correct hashes/nix/store contentspatches/ directory, referenced in derivationStandard derivation template:
{ lib
, stdenv
, fetchFromGitHub
, buildRustPackage # or buildPythonPackage, buildGoModule, etc.
, pkg-config
, openssl
, ...
}:
buildRustPackage rec {
pname = "package-name";
version = "1.0.0";
src = fetchFromGitHub {
owner = "owner-name";
repo = "repo-name";
rev = "v${version}";
hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
cargoHash = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=";
nativeBuildInputs = [ pkg-config ];
buildInputs = [ openssl ];
meta = with lib; {
description = "Short package description";
homepage = "https://example.com";
license = licenses.mit;
maintainers = with maintainers; [ your-handle ];
mainProgram = "executable-name";
};
}
nativeBuildInputs: Tools needed at build time (run on build platform)
pkg-config, cmake, cargo, rustc, makeWrapperbuildInputs: Libraries needed at build AND runtime (target platform)
openssl, sqlite, postgresqlpropagatedBuildInputs: Dependencies that consumers also need
checkInputs: Dependencies only for running tests
pytest, cargo-nextestdoCheck = truebuildRustPackage rec {
pname = "rust-app";
version = "1.0.0";
src = fetchFromGitHub { ... };
cargoHash = "sha256-..."; # Use lib.fakeHash initially, update after first build
# Optional: Lock file handling
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
"dependency-0.1.0" = "sha256-...";
};
};
# Build-time flags
cargoBuildFlags = [ "--all-features" ];
# Skip tests if they require network/special setup
doCheck = false;
meta = { ... };
}
buildPythonPackage rec {
pname = "python-pkg";
version = "1.0.0";
pyproject = true; # For pyproject.toml-based projects
src = fetchPypi {
inherit pname version;
hash = "sha256-...";
};
build-system = [ setuptools ]; # or hatchling, poetry-core, etc.
dependencies = [
requests
pydantic
];
optional-dependencies = {
dev = [ pytest black ];
};
pythonImportsCheck = [ "module_name" ];
meta = { ... };
}
buildNpmPackage rec {
pname = "node-app";
version = "1.0.0";
src = fetchFromGitHub { ... };
npmDepsHash = "sha256-...";
# Optional: Custom build phase
buildPhase = ''
npm run build
'';
installPhase = ''
mkdir -p $out/bin
cp -r dist $out/
makeWrapper ${nodejs}/bin/node $out/bin/${pname} \
--add-flags "$out/dist/index.js"
'';
meta = { ... };
}
src = fetchFromGitHub {
owner = "owner-name";
repo = "repo-name";
rev = "v${version}"; # or commit hash
hash = "sha256-..."; # Use lib.fakeHash initially
# Optional:
fetchSubmodules = true;
};
src = fetchurl {
url = "https://example.com/file-${version}.tar.gz";
hash = "sha256-...";
};
src = fetchPypi {
inherit pname version;
hash = "sha256-...";
# Optional:
extension = "zip";
};
Getting correct hashes:
lib.fakeHash or lib.fakeSha256 initially# Initial attempt
hash = lib.fakeHash;
# After failure, copy the hash from error message
hash = "sha256-Abc123...";
For Rust crates:
# Prefetch cargo dependencies
nix-prefetch-url --unpack "https://crates.io/api/v1/crates/package/1.0.0/download"
Standard flake template:
{
description = "Package or system description";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
packages.default = pkgs.callPackage ./default.nix { };
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
rustc
cargo
pkg-config
openssl
];
};
# For NixOS modules
nixosModules.default = import ./module.nix;
});
}
# Update all inputs
nix flake update
# Update specific input
nix flake lock --update-input nixpkgs
# Show flake metadata
nix flake metadata
# Check flake for issues
nix flake check
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.myservice;
in
{
options.services.myservice = {
enable = mkEnableOption "myservice";
package = mkOption {
type = types.package;
default = pkgs.myservice;
description = "Package to use for myservice";
};
port = mkOption {
type = types.port;
default = 8080;
description = "Port to listen on";
};
settings = mkOption {
type = types.attrs;
default = { };
description = "Additional settings";
};
};
config = mkIf cfg.enable {
systemd.services.myservice = {
description = "My Service";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/myservice --port ${toString cfg.port}";
Restart = "always";
User = "myservice";
Group = "myservice";
};
};
users.users.myservice = {
isSystemUser = true;
group = "myservice";
};
users.groups.myservice = { };
};
}
Service with configuration file:
config = mkIf cfg.enable {
environment.etc."myservice/config.json".text = builtins.toJSON cfg.settings;
systemd.services.myservice = {
serviceConfig = {
ExecStart = "${cfg.package}/bin/myservice --config /etc/myservice/config.json";
};
};
};
Conditional package installation:
environment.systemPackages = optional cfg.enable cfg.package;
# overlays/default.nix