Back to skills
SKILL.md
Nix Best Practices
ASecurityUse when working with Nix flakes, overlays, shell.nix, or flake.nix files.
- 2 stars
- 0 votes
- 0 copies
- 0 views
- Added October 16, 2026
Security analysis
100/100npx -y skills add alleneubank/agent-profile --skill nix-best-practices --agent claude-codeAre you the author of Nix Best Practices?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/alleneubank-nix-best-practices)---
name: nix-best-practices
description: Use when working with Nix flakes, overlays, shell.nix, or flake.nix files.
---
# Nix Best Practices
## Flake Structure
Standard flake.nix structure:
```nix
{
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:
```nix
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:
```nix
outputs = { self, nixpkgs, some-overlay, another-overlay, ... }:
```
## Applying Overlays
Overlays modify or add packages to nixpkgs:
```nix
let
pkgs = import nixpkgs {
inherit system;
overlays = [
overlay1.overlays.default
overlay2.overlays.default
# Inline overlay
(final: prev: {
myPackage = prev.myPackage.override { ... };
})
];
};
in
```
## Handling Unfree Packages
### Option 1: nixpkgs-unfree (Recommended for Teams)
Use numtide/nixpkgs-unfree for EULA-licensed packages without requiring user config:
```nix
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`:
```nix
{ allowUnfree = true; }
```
### Option 3: Specific Packages (Flake)
```nix
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
"specific-package"
];
};
in
```
Note: `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
```nix
{
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
```bash
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:
```bash
nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0
```
## Dev Shell Patterns
### Basic Shell
```nix
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
nodejs
python3
];
shellHook = ''
echo "Dev environment ready"
'';
};
```
### With Environment Variables
```nix
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)
```nix
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:
```bash
use flake
```
For unfree packages without nixpkgs-unfree:
```bash
export NIXPKGS_ALLOW_UNFREE=1
use flake --impure
```
## Troubleshooting
### "unexpected argument" Error
All inputs must be listed in outputs function:
```nix
# 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:
```nix
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.
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.