
Nix Best Practices
- 459 installs
- 52 repo stars
- Updated June 24, 2026
- 0xbigboss/claude-code
nix-best-practices is an agent skill that configures reproducible Nix flakes, overlays, derivations, and dev shells for developers scaffolding hermetic build environments and cross-language CI tooling in polyglot reposit
About
nix-best-practices is a Claude Code skill for working with Nix flakes, overlays, shell.nix, and flake.nix files in polyglot repositories. It documents a standard flake.nix skeleton using nixpkgs and flake-utils, follows patterns to deduplicate nixpkgs inputs, and shows how to apply overlay chains including inline final-prev overrides. Dev shell recipes cover mkShell buildInputs, shellHook environment exports, native library PKG_CONFIG_PATH wiring, and direnv use flake integration with NIXPKGS_ALLOW_UNFREE for unfree packages. A binary overlay template fetches platform-specific releases for x86_64-linux, aarch64-linux, x86_64-darwin, and aarch64-darwin with nix-prefetch-url hash workflows. Common commands include nix flake update, nix flake check, nix develop, nix build, and nix run. Developers reach for nix-best-practices when agents scaffold hermetic CI pipelines or cross-language build tooling where config.allowUnfree in flake.nix fails under nix develop.
- flakes
- reproducible builds
- derivations
- dev shells
- CI caching
Nix Best Practices by the numbers
- 459 all-time installs (skills.sh)
- Ranked #269 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/0xbigboss/claude-code --skill nix-best-practicesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 459 |
|---|---|
| repo stars | ★ 52 |
| Last updated | June 24, 2026 |
| Repository | 0xbigboss/claude-code ↗ |
How do you structure a Nix flake for dev shells?
Configure reproducible Nix flakes, derivations, and dev shells when agents scaffold hermetic environments, CI pipelines, or cross-language build tooling for polyglot repos.
Who is it for?
Developers standardizing hermetic Nix flake environments across polyglot repos who need overlay, dev shell, and binary packaging patterns agents can apply consistently.
Skip if: Teams not using Nix or developers only needing Docker-based devcontainers without flakes, overlays, or nix develop workflows.
When should I use this skill?
The user edits flake.nix, shell.nix, overlay inputs, mkShell definitions, or asks for reproducible Nix dev environments and CI build shells.
What you get
flake.nix with deduplicated inputs, overlay-applied nixpkgs, mkShell devShells, optional direnv .envrc, and nix flake check-passing configuration.
- flake.nix with inputs and devShells
- Overlay definitions for custom or binary packages
- direnv .envrc for automatic shell entry
By the numbers
- Binary overlay template covers 4 target platforms
- Documents 3 unfree-package handling options including nixpkgs-unfree
- Lists 6 core Nix commands: flake update, flake check, develop, build, run, prefetch-url
Files
Nix Best Practices
Flake Structure
Standard flake.nix structure:
{
description = "Project 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 = import nixpkgs {
inherit system;
};
in {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
# packages here
];
};
});
}Follows Pattern (Avoid Duplicate Nixpkgs)
When adding overlay inputs, use follows to share the parent nixpkgs and avoid downloading multiple versions:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
# Overlay follows parent nixpkgs
some-overlay.url = "github:owner/some-overlay";
some-overlay.inputs.nixpkgs.follows = "nixpkgs";
# Chain follows through intermediate inputs
another-overlay.url = "github:owner/another-overlay";
another-overlay.inputs.nixpkgs.follows = "some-overlay";
};All inputs must be listed in outputs function even if not directly used:
outputs = { self, nixpkgs, some-overlay, another-overlay, ... }:Applying Overlays
Overlays modify or add packages to nixpkgs:
let
pkgs = import nixpkgs {
inherit system;
overlays = [
overlay1.overlays.default
overlay2.overlays.default
# Inline overlay
(final: prev: {
myPackage = prev.myPackage.override { ... };
})
];
};
inHandling Unfree Packages
Option 1: nixpkgs-unfree (Recommended for Teams)
Use numtide/nixpkgs-unfree for EULA-licensed packages without requiring user config:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-unfree.url = "github:numtide/nixpkgs-unfree/nixos-unstable";
nixpkgs-unfree.inputs.nixpkgs.follows = "nixpkgs";
# Unfree overlay follows nixpkgs-unfree
proprietary-tool.url = "github:owner/proprietary-tool-overlay";
proprietary-tool.inputs.nixpkgs.follows = "nixpkgs-unfree";
};This chains: proprietary-tool → nixpkgs-unfree → nixpkgs
Option 2: User Config
Users add to ~/.config/nixpkgs/config.nix:
{ allowUnfree = true; }Option 3: Specific Packages (Flake)
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
"specific-package"
];
};
inNote: config.allowUnfree in flake.nix doesn't work with nix develop - use nixpkgs-unfree or user config.
Creating Binary Overlay Repos
When nixpkgs builds a community version lacking features (common with open-core tools), create an overlay that fetches official binaries.
Pattern (see 0xBigBoss/atlas-overlay, 0xBigBoss/bun-overlay)
{
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};
version = "1.0.0";
# Platform-specific binaries
sources = {
"x86_64-linux" = {
url = "https://example.com/tool-linux-amd64-v${version}";
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
"aarch64-linux" = {
url = "https://example.com/tool-linux-arm64-v${version}";
sha256 = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=";
};
"x86_64-darwin" = {
url = "https://example.com/tool-darwin-amd64-v${version}";
sha256 = "sha256-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=";
};
"aarch64-darwin" = {
url = "https://example.com/tool-darwin-arm64-v${version}";
sha256 = "sha256-DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=";
};
};
source = sources.${system} or (throw "Unsupported system: ${system}");
toolPackage = pkgs.stdenv.mkDerivation {
pname = "tool";
inherit version;
src = pkgs.fetchurl {
inherit (source) url sha256;
};
sourceRoot = ".";
dontUnpack = true;
installPhase = ''
mkdir -p $out/bin
cp $src $out/bin/tool
chmod +x $out/bin/tool
'';
meta = with pkgs.lib; {
description = "Tool description";
homepage = "https://example.com";
license = licenses.unfree; # or appropriate license
platforms = builtins.attrNames sources;
};
};
in {
packages.default = toolPackage;
packages.tool = toolPackage;
overlays.default = final: prev: {
tool = toolPackage;
};
})
// {
overlays.default = final: prev: {
tool = self.packages.${prev.system}.tool;
};
};
}Getting SHA256 Hashes
nix-prefetch-url https://example.com/tool-linux-amd64-v1.0.0
# Returns hash in base32, convert to SRI format:
nix hash to-sri --type sha256 <base32-hash>Or use SRI directly:
nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0Dev Shell Patterns
Basic Shell
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs
python3
];
shellHook = ''
echo "Dev environment ready"
'';
};With Environment Variables
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ postgresql ];
# Set at shell entry
DATABASE_URL = "postgres://localhost/dev";
# Or in shellHook for dynamic values
shellHook = ''
export PROJECT_ROOT="$(pwd)"
'';
};Native Dependencies (C Libraries)
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
openssl
postgresql
];
# Expose headers and libraries
shellHook = ''
export C_INCLUDE_PATH="${pkgs.openssl.dev}/include:$C_INCLUDE_PATH"
export LIBRARY_PATH="${pkgs.openssl.out}/lib:$LIBRARY_PATH"
export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"
'';
};Direnv Integration
.envrc for flake projects:
use flakeFor unfree packages without nixpkgs-unfree:
export NIXPKGS_ALLOW_UNFREE=1
use flake --impureCommon Commands
# Update all inputs
nix flake update
# Update specific input
nix flake update some-input
# Check flake validity
nix flake check
# Show flake metadata
nix flake metadata
# Enter dev shell
nix develop
# Run command in dev shell
nix develop -c <command>
# Build package
nix build .#packageName
# Run package
nix run .#packageNameTroubleshooting
"unexpected argument" Error
All inputs must be listed in outputs function:
# Wrong
outputs = { self, nixpkgs }: ...
# Right (if you have more inputs)
outputs = { self, nixpkgs, other-input, ... }: ...Unfree Package Errors with nix develop
config.allowUnfree in flake.nix doesn't propagate to nix develop. Use: 1. nixpkgs-unfree input (recommended) 2. User's ~/.config/nixpkgs/config.nix 3. NIXPKGS_ALLOW_UNFREE=1 nix develop --impure
Duplicate Nixpkgs Downloads
Use follows to chain inputs to a single nixpkgs source.
Overlay Not Applied
Ensure overlay is in the overlays list when importing nixpkgs:
pkgs = import nixpkgs {
inherit system;
overlays = [ my-overlay.overlays.default ];
};Hash Mismatch
Re-fetch with nix-prefetch-url and update the hash. Hashes change when upstream updates binaries at the same URL.
Related skills
How it compares
Use nix-best-practices for flake-native reproducible shells; choose Docker devcontainer skills when the team standardizes on container images instead of Nix derivations.
FAQ
What flake structure does nix-best-practices recommend?
nix-best-practices starts from a flake.nix using nixpkgs and flake-utils with eachDefaultSystem, defining devShells.default via pkgs.mkShell and listing every input in the outputs function to avoid unexpected-argument errors.
How does nix-best-practices handle unfree packages?
nix-best-practices recommends numtide/nixpkgs-unfree with follows chains for team projects. config.allowUnfree inside flake.nix does not propagate to nix develop, so direnv may need NIXPKGS_ALLOW_UNFREE=1 with use flake --impure.
How do you fetch binary releases in Nix with this skill?
nix-best-practices provides a stdenv.mkDerivation overlay template with per-platform fetchurl sources and nix-prefetch-url plus nix hash to-sri commands to populate sha256 hashes for Linux and macOS binaries.