
Nix
- 1 installs
- Updated January 23, 2026
- aeshakhzod/nixos-and-flakes-skill
nix is a Claude Code skill for NixOS, Nix Flakes, Home Manager, and nix-darwin, covering declarative system configuration and reproducible cross-platform environments.
About
nix is a Claude Code skill for NixOS, Nix Flakes, Home Manager, and nix-darwin. It covers declarative system configuration, reproducible environments pinned by flake.lock, the module system, package overrides and overlays, and cross-platform (Linux/macOS) workflows. Developers use it to build and maintain reproducible machines and dev shells and to recover via NixOS generation rollback.
- Covers NixOS, Nix Flakes, Home Manager, and nix-darwin declarative configuration
- Reproducible environments via flake.lock plus rollback through NixOS generations
- Command reference for rebuild, dev shells, updates, and GC, with common gotchas
Nix by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,173 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
nix capabilities & compatibility
Free open-source toolchain.
- Capabilities
- declarative config · reproducible environments · package management
- Works with
- github
- Use cases
- devops
- Platforms
- Linux · macOS
- Pricing
- Free
What nix says it does
Covers declarative system configuration, reproducible environments, package management, and cross-platform Nix workflows.
**Untracked files ignored** - `git add` before any flake command
npx skills add https://github.com/aeshakhzod/nixos-and-flakes-skill --skill nixAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| Last updated | January 23, 2026 |
| Repository | aeshakhzod/nixos-and-flakes-skill ↗ |
What it does
Write and maintain declarative, reproducible NixOS, Home Manager, or nix-darwin configurations and dev shells across Linux and macOS.
Who is it for?
Declarative, reproducible NixOS/Home Manager/nix-darwin configs and dev shells.
Skip if: Imperative, step-by-step system setup rather than declared desired state.
When should I use this skill?
You are doing any Nix, NixOS, Flakes, Home Manager, or nix-darwin task.
By the numbers
- 7 common gotchas listed
- 9 reference files (flakes, home-manager, nix-darwin, nix-language, etc.)
Files
Nix Ecosystem Guide
Core Philosophy
1. Declarative over Imperative - Describe desired state, not steps to reach it 2. Reproducibility - Lock files (flake.lock) pin exact versions 3. Immutability - Nix Store is read-only; same inputs = same outputs 4. Rollback (NixOS) - Every generation preserved; instant recovery via boot menu
Flake Structure
{
description = "My Nix configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs"; # CRITICAL: avoid duplicate nixpkgs
};
# macOS support
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager, nix-darwin, ... }@inputs: {
# NixOS configurations
nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./configuration.nix ];
};
# macOS configurations
darwinConfigurations.hostname = nix-darwin.lib.darwinSystem {
system = "aarch64-darwin"; # or x86_64-darwin for Intel
modules = [ ./darwin.nix ];
};
# Development shells
devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
packages = [ /* ... */ ];
};
};
}Essential Patterns
Input Management
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
# Use parent's nixpkgs to avoid downloading multiple versions
home-manager.inputs.nixpkgs.follows = "nixpkgs";
# Non-flake input (config files, etc.)
private-config = {
url = "git+ssh://git@github.com/user/config.git";
flake = false;
};
};Module System
# Modules have: imports, options, config
{ config, pkgs, lib, ... }: {
imports = [ ./hardware.nix ./services.nix ];
options.myOption = lib.mkOption {
type = lib.types.bool;
default = false;
};
config = lib.mkIf config.myOption {
# conditional configuration
};
}Priority Control
{
# lib.mkDefault (priority 1000) - base module defaults
services.nginx.enable = lib.mkDefault true;
# Direct assignment (priority 100) - normal config
services.nginx.enable = true;
# lib.mkForce (priority 50) - override everything
services.nginx.enable = lib.mkForce false;
}Package Customization
{
# Override function arguments
pkgs.fcitx5-rime.override { rimeDataPkgs = [ ./custom-rime ]; }
# Override derivation attributes
pkgs.hello.overrideAttrs (old: { doCheck = false; })
# Overlays (global modification)
nixpkgs.overlays = [
(final: prev: {
myPackage = prev.myPackage.override { /* ... */ };
})
];
}Platform-Specific
NixOS
sudo nixos-rebuild switch --flake .#hostname
sudo nixos-rebuild boot --flake .#hostname # apply on next boot
sudo nixos-rebuild test --flake .#hostname # test without boot entrynix-darwin (macOS)
darwin-rebuild switch --flake .#hostname
# TouchID for sudo:
# security.pam.services.sudo_local.touchIdAuth = true;Home Manager
# As NixOS/Darwin module:
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home.nix;
# Standalone:
home-manager switch --flake .#username@hostnameCommands Reference
| Task | Command |
|---|---|
| Rebuild NixOS | sudo nixos-rebuild switch --flake .#hostname |
| Rebuild Darwin | darwin-rebuild switch --flake .#hostname |
| Dev shell | nix develop |
| Temp package | nix shell nixpkgs#package |
| Run package | nix run nixpkgs#package |
| Update all | nix flake update |
| Update one | nix flake update nixpkgs |
| GC old gens | sudo nix-collect-garbage -d |
| List gens | nix profile history --profile /nix/var/nix/profiles/system |
| Debug build | nixos-rebuild switch --show-trace -L -v |
| REPL | nix repl then :lf . to load flake |
Common Gotchas
1. Untracked files ignored - git add before any flake command (nix build/run/shell/develop, nixos-rebuild, darwin-rebuild) 2. allowUnfree fails in devShells - Use nixpkgs-unfree overlay or ~/.config/nixpkgs/config.nix 3. Duplicate input downloads - Use follows to pin dependencies (most common: inputs.nixpkgs.follows) 4. Python pip fails - Use venv, poetry2nix, or containers 5. Downloaded binaries fail - Use FHS environment or nix-ld 6. Merge conflicts in lists - Use lib.mkBefore/lib.mkAfter for ordering 7. Build from source unexpectedly - Check if overlays invalidate cache
Development Environments
# In flake.nix outputs:
devShells.x86_64-linux.default = pkgs.mkShell {
packages = with pkgs; [ nodejs python3 rustc ];
shellHook = ''
echo "Dev environment ready"
export MY_VAR="value"
'';
# For C libraries
LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.openssl ];
};direnv Integration
# .envrc
use flake
# or for unfree: use flake --impureDebugging
# Verbose rebuild
nixos-rebuild switch --show-trace --print-build-logs --verbose
# Interactive REPL
nix repl
:lf . # load current flake
:e pkgs.hello # open in editor
:b pkgs.hello # build derivation
inputs.<TAB> # explore inputsReferences
For detailed information, see:
references/nix-language.md- Nix language syntaxreferences/flakes.md- Flake inputs/outputs detailsreferences/home-manager.md- User environment managementreferences/nix-darwin.md- macOS configurationreferences/nixpkgs-advanced.md- Overlays, overrides, callPackagereferences/dev-environments.md- Dev shells, direnv, FHSreferences/best-practices.md- Modularization, debugging, deploymentreferences/templates.md- Ready-to-use flake.nix examples
Best Practices
Configuration Organization
Modularization
Split large configurations into modules:
nixos-config/
├── flake.nix
├── flake.lock
├── hosts/
│ ├── desktop/
│ │ ├── default.nix
│ │ └── hardware-configuration.nix
│ └── laptop/
│ ├── default.nix
│ └── hardware-configuration.nix
├── modules/
│ ├── common.nix
│ ├── desktop.nix
│ ├── development.nix
│ └── services/
│ ├── nginx.nix
│ └── postgres.nix
├── home/
│ ├── default.nix
│ ├── shell.nix
│ └── programs/
│ ├── git.nix
│ ├── neovim.nix
│ └── tmux.nix
└── overlays/
└── default.nixModule Pattern
# modules/development.nix
{ config, lib, pkgs, ... }: {
options.myConfig.development = {
enable = lib.mkEnableOption "development tools";
};
config = lib.mkIf config.myConfig.development.enable {
environment.systemPackages = with pkgs; [
git vim nodejs
];
};
}
# hosts/desktop/default.nix
{
imports = [ ../../modules/development.nix ];
myConfig.development.enable = true;
}Git Integration
Version Control Your Config
# Initialize git in config directory
cd ~/nixos-config
git init
git add .
git commit -m "Initial config"Critical: Stage Files Before Build
Nix ignores untracked files in flakes:
# This FAILS if new files aren't staged
sudo nixos-rebuild switch --flake .
# Always stage first
git add .
sudo nixos-rebuild switch --flake .Move Config from /etc/nixos
# Option 1: Symlink
sudo mv /etc/nixos /etc/nixos.bak
sudo ln -s ~/nixos-config /etc/nixos
# Option 2: Specify path directly
sudo nixos-rebuild switch --flake ~/nixos-config#hostnameDebugging
Verbose Output
# Full debug output
sudo nixos-rebuild switch --flake .#hostname --show-trace --print-build-logs --verbose
# Shorthand
sudo nixos-rebuild switch --flake .#hostname --show-trace -L -vnix repl
Interactive debugging:
nix repl
# Load current flake
:lf .
# Explore structure
inputs.<TAB>
outputs.<TAB>
nixosConfigurations.hostname.config.services.<TAB>
# Open package in editor
:e pkgs.hello
# Build derivation
:b pkgs.hello
# Show logs
:log pkgs.hello
# Get derivation path
builtins.toString pkgs.helloCommon Errors
| Error | Cause | Fix |
|---|---|---|
| "file not found" | Untracked file | git add . |
| "infinite recursion" | Self-referential config | Check final vs prev in overlays |
| "collision between" | Duplicate packages | Split into different profiles |
| "hash mismatch" | Source changed | Update hash in fetchurl/fetchFromGitHub |
System Management
Generation Management
# List all generations
nix profile history --profile /nix/var/nix/profiles/system
# Delete old generations
sudo nix profile wipe-history --older-than 7d --profile /nix/var/nix/profiles/system
# Garbage collect
sudo nix-collect-garbage -d
# Or just GC without deleting generations
sudo nix store gcAutomatic GC
# In configuration.nix
{
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
};
# Optimize store
nix.optimise.automatic = true;
}Boot Entries
{
# Limit boot menu entries
boot.loader.systemd-boot.configurationLimit = 10;
# Or for GRUB
boot.loader.grub.configurationLimit = 10;
}Input Management
Pin Versions
# Update all inputs
nix flake update
# Update single input
nix flake update nixpkgs
# Lock to specific commit
nix flake lock --override-input nixpkgs github:NixOS/nixpkgs/abc123Use follows
Always use follows to avoid multiple nixpkgs:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
# Every input that uses nixpkgs should follow
};Mixing Stable and Unstable
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { nixpkgs, nixpkgs-unstable, ... }: {
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
modules = [{
nixpkgs.overlays = [
(final: prev: {
unstable = nixpkgs-unstable.legacyPackages.${prev.system};
})
];
# Use stable by default
environment.systemPackages = [ pkgs.vim ];
# Use unstable for specific packages
programs.firefox.package = pkgs.unstable.firefox;
}];
};
};Remote Deployment
nixos-rebuild
# Deploy to remote host
nixos-rebuild switch --flake .#remote-host \
--target-host user@remote \
--build-host localhost # Build locally, deploy resultColmena
# flake.nix
{
outputs = { nixpkgs, ... }: {
colmena = {
meta = {
nixpkgs = import nixpkgs { system = "x86_64-linux"; };
};
host1 = {
deployment = {
targetHost = "192.168.1.10";
targetUser = "root";
};
imports = [ ./hosts/host1 ];
};
};
};
}
# Deploy
nix run nixpkgs#colmena -- applyBinary Cache
Using Cachix
# Install cachix
nix-env -iA cachix -f https://cachix.org/api/v1/install
# Use a cache
cachix use nix-community
# Or in configuration
nix.settings = {
substituters = [
"https://cache.nixos.org"
"https://nix-community.cachix.org"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
};Security
Secrets Management
Never commit secrets to git. Options:
1. sops-nix - Encrypted secrets in repo 2. agenix - Age-encrypted secrets 3. Environment variables - Runtime injection
# Example with sops-nix
{
sops.secrets.my-secret = {
sopsFile = ./secrets.yaml;
};
services.myservice.passwordFile = config.sops.secrets.my-secret.path;
}Principle of Least Privilege
{
# Run services as unprivileged users
systemd.services.myservice = {
serviceConfig = {
DynamicUser = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
};
};
}Common Patterns
Conditional Configuration
{ lib, config, ... }: {
config = lib.mkMerge [
# Always applied
{ environment.systemPackages = [ pkgs.vim ]; }
# Conditional
(lib.mkIf config.services.xserver.enable {
environment.systemPackages = [ pkgs.firefox ];
})
];
}Platform-Specific
{ pkgs, lib, ... }: {
environment.systemPackages = with pkgs; [
git
vim
] ++ lib.optionals pkgs.stdenv.isLinux [
# Linux only
inotify-tools
] ++ lib.optionals pkgs.stdenv.isDarwin [
# macOS only
darwin.apple_sdk.frameworks.Security
];
}DRY with Functions
# lib/mkHost.nix
{ inputs }: hostname: {
system,
modules ? [],
...
}:
inputs.nixpkgs.lib.nixosSystem {
inherit system;
modules = [
../hosts/${hostname}
../modules/common.nix
] ++ modules;
specialArgs = { inherit inputs; };
}
# flake.nix
{
nixosConfigurations = {
desktop = mkHost "desktop" { system = "x86_64-linux"; };
laptop = mkHost "laptop" { system = "x86_64-linux"; };
};
}Development Environments
Overview
Three approaches for dev environments: 1. nix shell - Quick, temporary access to packages 2. nix develop - Full dev shell with build inputs 3. direnv - Automatic environment on directory entry
nix shell
Temporary shell with packages available:
# Single package
nix shell nixpkgs#nodejs
# Multiple packages
nix shell nixpkgs#nodejs nixpkgs#yarn nixpkgs#python3
# Run command directly
nix shell nixpkgs#cowsay --command cowsay "Hello"
# From specific nixpkgs version
nix shell github:NixOS/nixpkgs/nixos-24.11#nodejsnix run
Run package without installing:
# Run default program
nix run nixpkgs#hello
# Run specific program from package
nix run nixpkgs#python3 -- script.py
# Run from flake
nix run .#myAppnix develop
Enter development shell defined in flake:
# Default devShell
nix develop
# Named devShell
nix develop .#python
# From remote flake
nix develop github:owner/repo
# Run command without entering shell
nix develop --command bash -c "npm install && npm test"pkgs.mkShell
Define development environment in flake.nix:
{
outputs = { nixpkgs, ... }: let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
in {
devShells.x86_64-linux.default = pkgs.mkShell {
# Packages available in shell
packages = with pkgs; [
nodejs_20
yarn
python3
go
rustc
cargo
];
# Build inputs (for compiling native extensions)
buildInputs = with pkgs; [
openssl
zlib
];
# Native build inputs (build tools)
nativeBuildInputs = with pkgs; [
pkg-config
cmake
];
# Environment variables
MY_VAR = "value";
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
# Shell hook (runs on entry)
shellHook = ''
echo "Welcome to dev environment!"
export PATH="$PWD/node_modules/.bin:$PATH"
'';
# For C library headers
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.openssl
pkgs.zlib
];
};
};
}Multi-Platform Support
{
outputs = { nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.default = pkgs.mkShell {
packages = with pkgs; [ nodejs ];
};
});
}Or manually:
{
outputs = { nixpkgs, ... }: let
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f {
pkgs = nixpkgs.legacyPackages.${system};
});
in {
devShells = forAllSystems ({ pkgs }: {
default = pkgs.mkShell { packages = [ pkgs.nodejs ]; };
});
};
}Multiple Dev Shells
{
devShells.x86_64-linux = {
default = pkgs.mkShell {
packages = [ pkgs.nodejs ];
};
python = pkgs.mkShell {
packages = [ pkgs.python3 pkgs.poetry ];
};
rust = pkgs.mkShell {
packages = [ pkgs.rustc pkgs.cargo pkgs.rust-analyzer ];
};
};
}
# Usage:
# nix develop # default
# nix develop .#python
# nix develop .#rustdirenv Integration
Automatic environment activation:
Setup
# In home.nix or configuration.nix
programs.direnv = {
enable = true;
nix-direnv.enable = true; # Better caching
};Usage
# In project root, create .envrc
echo "use flake" > .envrc
# Allow direnv
direnv allow
# Now environment activates automatically on cdAdvanced .envrc
# Basic
use flake
# With impure (for unfree packages)
use flake --impure
# Specific devShell
use flake .#python
# From remote
use flake github:owner/repo
# Watch additional files (rebuild on change)
watch_file flake.nix
watch_file flake.lock
# Additional env vars
export MY_VAR="value"
# Load .env file
dotenv_if_existsFHS Environment (Downloaded Binaries)
NixOS doesn't follow standard Linux paths. For prebuilt binaries:
{ pkgs, ... }: {
# Add to environment
environment.systemPackages = [
(pkgs.buildFHSEnv {
name = "fhs";
targetPkgs = pkgs: with pkgs; [
# Common requirements
zlib
glib
# Add what your binary needs
openssl
curl
libGL
xorg.libX11
];
runScript = "bash";
})
];
}
# Usage: enter with `fhs`, then run binaries normallyFor Specific Binary
{
myBinary = pkgs.buildFHSEnv {
name = "my-binary";
targetPkgs = pkgs: [ pkgs.zlib ];
runScript = "${./my-binary}";
};
}nix-ld (Alternative for Binaries)
System-wide dynamic linker for unpatched binaries:
# In NixOS configuration
{
programs.nix-ld = {
enable = true;
libraries = with pkgs; [
stdenv.cc.cc
zlib
openssl
curl
];
};
}Python Development
Python packages installed via pip fail on NixOS. Solutions:
Virtual Environment
devShells.default = pkgs.mkShell {
packages = [ pkgs.python3 ];
shellHook = ''
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
'';
};poetry2nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
poetry2nix.url = "github:nix-community/poetry2nix";
};
outputs = { nixpkgs, poetry2nix, ... }: let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
p2n = poetry2nix.lib.mkPoetry2Nix { inherit pkgs; };
in {
devShells.x86_64-linux.default = p2n.mkPoetryEnv {
projectDir = ./.;
};
};
}Language-Specific Shells
Node.js
pkgs.mkShell {
packages = with pkgs; [ nodejs_20 yarn nodePackages.pnpm ];
shellHook = ''
export PATH="$PWD/node_modules/.bin:$PATH"
'';
}Rust
pkgs.mkShell {
packages = with pkgs; [
rustc cargo rust-analyzer rustfmt clippy
];
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
}Go
pkgs.mkShell {
packages = with pkgs; [ go gopls gotools go-tools ];
shellHook = ''
export GOPATH="$PWD/.go"
export PATH="$GOPATH/bin:$PATH"
'';
}C/C++
pkgs.mkShell {
packages = with pkgs; [ gcc cmake gnumake gdb ];
buildInputs = with pkgs; [ openssl zlib ];
nativeBuildInputs = [ pkgs.pkg-config ];
}Community Templates
Use existing templates instead of writing from scratch:
# List available templates
nix flake show templates
# Initialize from template
nix flake init -t github:the-nix-way/dev-templates#rust
nix flake init -t github:the-nix-way/dev-templates#node
nix flake init -t github:the-nix-way/dev-templates#pythonFlakes Reference
Overview
Flakes provide:
- Hermetic evaluation - No impure operations
- Lock file - Reproducible dependency versions
- Standard structure - Consistent
inputs/outputsschema - Composability - Easy to combine multiple flakes
Enabling Flakes
# In configuration.nix or nix.conf
nix.settings.experimental-features = [ "nix-command" "flakes" ];Input Types
GitHub
inputs = {
# Default branch
nixpkgs.url = "github:NixOS/nixpkgs";
# Specific branch
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
# Specific commit
nixpkgs.url = "github:NixOS/nixpkgs/abc123def456";
# Specific tag
nixpkgs.url = "github:NixOS/nixpkgs/24.11";
# Private repo (uses SSH)
private.url = "github:owner/private-repo";
};Git
inputs = {
# HTTPS
repo.url = "git+https://git.example.com/repo.git";
# SSH
repo.url = "git+ssh://git@github.com/owner/repo.git";
# Specific branch
repo.url = "git+https://example.com/repo?ref=develop";
# Specific tag
repo.url = "git+https://example.com/repo?tag=v1.0.0";
# Specific commit
repo.url = "git+https://example.com/repo?rev=abc123";
# Shallow clone
repo.url = "git+ssh://git@github.com/owner/repo?shallow=1";
};Path (Local)
inputs = {
# Local directory
local.url = "path:/home/user/projects/my-flake";
# Relative (from flake root)
local.url = "path:./subdir";
};Tarball
inputs = {
archive.url = "https://example.com/archive.tar.gz";
};Non-Flake Inputs
inputs = {
# Config files, data, etc.
dotfiles = {
url = "github:user/dotfiles";
flake = false; # Don't evaluate as flake
};
};
# Usage in outputs:
outputs = { dotfiles, ... }: {
# Reference files directly
home.file.".vimrc".source = "${dotfiles}/vimrc";
};Input Follows
Prevents downloading multiple versions of the same dependency:
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs"; # Use OUR nixpkgs
};
# Nested follows
foo = {
url = "github:owner/foo";
inputs.nixpkgs.follows = "nixpkgs";
inputs.bar.follows = "bar"; # If foo has bar as input
};
};Flake Outputs Schema
outputs = { self, nixpkgs, ... }: {
# ===== Packages =====
packages.<system>.<name> = derivation;
packages.x86_64-linux.default = pkgs.hello;
packages.x86_64-linux.myApp = pkgs.callPackage ./app.nix {};
# ===== Applications =====
apps.<system>.<name> = {
type = "app";
program = "${package}/bin/executable";
};
# ===== Development Shells =====
devShells.<system>.<name> = pkgs.mkShell { ... };
devShells.x86_64-linux.default = pkgs.mkShell {
packages = [ pkgs.nodejs ];
};
# ===== NixOS Configurations =====
nixosConfigurations.<hostname> = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./configuration.nix ];
specialArgs = { inherit inputs; }; # Pass to modules
};
# ===== Darwin Configurations =====
darwinConfigurations.<hostname> = darwin.lib.darwinSystem {
system = "aarch64-darwin";
modules = [ ./darwin.nix ];
};
# ===== Home Manager Configurations =====
homeConfigurations."user@host" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [ ./home.nix ];
};
# ===== Overlays =====
overlays.<name> = final: prev: { ... };
overlays.default = final: prev: {
myPackage = prev.myPackage.override { ... };
};
# ===== NixOS/Darwin Modules =====
nixosModules.<name> = { config, ... }: { ... };
darwinModules.<name> = { config, ... }: { ... };
# ===== Templates =====
templates.<name> = {
path = ./template;
description = "A template";
};
templates.default = { ... };
# ===== Checks (CI) =====
checks.<system>.<name> = derivation;
# ===== Formatter =====
formatter.<system> = pkgs.nixpkgs-fmt; # or alejandra, nixfmt
# ===== Library Functions =====
lib = { ... };
# ===== Hydra Jobs =====
hydraJobs.<attr>.<system> = derivation;
};Lock File (flake.lock)
Auto-generated, contains exact versions:
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1234567890,
"narHash": "sha256-...",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "abc123...",
"type": "github"
}
}
}
}Flake Commands
# Initialize new flake
nix flake init
nix flake init -t templates#rust # From template
# Show flake info
nix flake show
nix flake show github:NixOS/nixpkgs
# Show flake metadata
nix flake metadata
# Update all inputs
nix flake update
# Update specific input
nix flake update nixpkgs
# Lock to specific version
nix flake lock --override-input nixpkgs github:NixOS/nixpkgs/abc123
# Check flake
nix flake check
# Build output
nix build .#packageName
nix build .#packages.x86_64-linux.default
# Run output
nix run .#appName
# Enter dev shell
nix develop
nix develop .#shellName
# Archive flake
nix flake archive
# Clone flake
nix flake clone github:owner/repo --dest ./localSelf Reference
The self input refers to the current flake:
outputs = { self, nixpkgs, ... }: {
packages.x86_64-linux.default = let
# Access other outputs
myLib = self.lib;
# Access flake source
src = self;
version = self.rev or self.dirtyRev or "unknown";
in
# ...
};Flake Registry
Named shortcuts for common flakes:
# List registry
nix registry list
# Add to registry
nix registry add myflake github:owner/repo
# Pin version
nix registry pin nixpkgs
# Remove
nix registry remove myflake
# Use in commands
nix shell nixpkgs#hello # Uses registry entry
nix shell github:NixOS/nixpkgs#hello # ExplicitHome Manager Reference
Overview
Home Manager manages user-specific:
- Packages in
~/.nix-profile - Dotfiles in
~/.config,~/.* - User services
- Shell configuration
Installation Methods
As NixOS Module
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, home-manager, ... }: {
nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home.nix;
# Pass extra args to home.nix
home-manager.extraSpecialArgs = { inherit inputs; };
}
];
};
};
}As Darwin Module
# Same pattern as NixOS
{
outputs = { nix-darwin, home-manager, ... }: {
darwinConfigurations.hostname = nix-darwin.lib.darwinSystem {
modules = [
./darwin.nix
home-manager.darwinModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home.nix;
}
];
};
};
}Standalone
# flake.nix
{
outputs = { nixpkgs, home-manager, ... }: {
homeConfigurations."user@hostname" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [ ./home.nix ];
extraSpecialArgs = { inherit inputs; };
};
};
}
# Apply with:
# home-manager switch --flake .#user@hostnameBasic home.nix
{ config, pkgs, ... }: {
home.username = "username";
home.homeDirectory = "/home/username"; # /Users/username on macOS
# Packages
home.packages = with pkgs; [
ripgrep
fd
jq
htop
];
# IMPORTANT: Match your Home Manager version
home.stateVersion = "24.11";
# Let Home Manager manage itself (standalone only)
programs.home-manager.enable = true;
}File Management
{
# Copy file
home.file.".config/app/config.toml".source = ./config.toml;
# Create from text
home.file.".config/app/config.toml".text = ''
[section]
key = "value"
'';
# Symlink
home.file.".config/app".source = config.lib.file.mkOutOfStoreSymlink ./app-config;
# Executable script
home.file.".local/bin/myscript" = {
executable = true;
text = ''
#!/bin/bash
echo "Hello"
'';
};
# Recursive directory
home.file.".config/nvim" = {
source = ./nvim;
recursive = true;
};
# XDG config (equivalent to ~/.config)
xdg.configFile."app/config.toml".source = ./config.toml;
}Program Modules
Home Manager has built-in modules for many programs:
Git
{
programs.git = {
enable = true;
userName = "Your Name";
userEmail = "you@example.com";
extraConfig = {
init.defaultBranch = "main";
pull.rebase = true;
push.autoSetupRemote = true;
};
aliases = {
co = "checkout";
st = "status";
};
ignores = [ ".DS_Store" "*.swp" ];
signing = {
key = "KEYID";
signByDefault = true;
};
delta.enable = true; # Better diffs
};
}Shell (Zsh)
{
programs.zsh = {
enable = true;
autosuggestion.enable = true;
syntaxHighlighting.enable = true;
shellAliases = {
ll = "ls -la";
update = "sudo nixos-rebuild switch --flake .#hostname";
};
initExtra = ''
# Custom init
export PATH="$HOME/.local/bin:$PATH"
'';
oh-my-zsh = {
enable = true;
plugins = [ "git" "docker" ];
theme = "robbyrussell";
};
};
}Shell (Fish)
{
programs.fish = {
enable = true;
shellAliases = { ll = "ls -la"; };
shellInit = ''
set -gx PATH $HOME/.local/bin $PATH
'';
plugins = [
{ name = "z"; src = pkgs.fishPlugins.z.src; }
];
};
}Neovim
{
programs.neovim = {
enable = true;
defaultEditor = true;
viAlias = true;
vimAlias = true;
plugins = with pkgs.vimPlugins; [
nvim-treesitter.withAllGrammars
telescope-nvim
nvim-lspconfig
];
extraLuaConfig = ''
-- Lua config here
vim.opt.number = true
'';
extraPackages = with pkgs; [
lua-language-server
nil # Nix LSP
];
};
}Starship Prompt
{
programs.starship = {
enable = true;
settings = {
add_newline = false;
character.success_symbol = "[➜](bold green)";
};
};
}Direnv
{
programs.direnv = {
enable = true;
nix-direnv.enable = true; # Better Nix integration
};
}Tmux
{
programs.tmux = {
enable = true;
clock24 = true;
baseIndex = 1;
terminal = "screen-256color";
plugins = with pkgs.tmuxPlugins; [
sensible
yank
];
extraConfig = ''
set -g mouse on
'';
};
}Environment Variables
{
home.sessionVariables = {
EDITOR = "nvim";
BROWSER = "firefox";
MY_VAR = "value";
};
# Path additions
home.sessionPath = [
"$HOME/.local/bin"
"$HOME/go/bin"
];
}User Services (systemd)
{
# Linux only
systemd.user.services.myservice = {
Unit.Description = "My Service";
Install.WantedBy = [ "default.target" ];
Service = {
ExecStart = "${pkgs.myapp}/bin/myapp";
Restart = "always";
};
};
}macOS (launchd)
{
# macOS only
launchd.agents.myservice = {
enable = true;
config = {
Program = "${pkgs.myapp}/bin/myapp";
RunAtLoad = true;
KeepAlive = true;
};
};
}Activation Scripts
{
home.activation = {
myScript = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
# Run after home-manager writes files
$DRY_RUN_CMD mkdir -p $HOME/.cache/myapp
'';
};
}NixOS vs Home Manager
| Aspect | NixOS | Home Manager |
|---|---|---|
| Scope | System-wide | Per-user |
| Requires | Root | No root needed |
| Services | systemd system | systemd user |
| Location | /etc, /run | ~/.config, ~/ |
| Packages | Available to all | User-specific |
Use NixOS for:
- System services (nginx, postgres)
- Hardware configuration
- Boot, kernel, networking
- System-wide packages
Use Home Manager for:
- User dotfiles
- User packages
- Shell configuration
- Desktop apps
- Portable configs (use across systems)
nix-darwin Reference
Overview
nix-darwin brings NixOS-style declarative configuration to macOS:
- System preferences
- Homebrew management
- launchd services
- User shell configuration
Installation
# Install Nix first (use Determinate Nix or official installer)
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh
# Create config directory
mkdir -p ~/.config/nix
cd ~/.config/nix
# Initialize flake from template
nix flake init -t nix-darwin
# Build and activate (first time)
nix run nix-darwin -- switch --flake .
# Subsequent rebuilds
darwin-rebuild switch --flake .Basic flake.nix
{
description = "Darwin configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-24.11-darwin";
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nix-darwin, nixpkgs }: {
darwinConfigurations."hostname" = nix-darwin.lib.darwinSystem {
system = "aarch64-darwin"; # Apple Silicon
# system = "x86_64-darwin"; # Intel Mac
modules = [ ./darwin.nix ];
};
};
}Basic darwin.nix
{ pkgs, ... }: {
# System packages
environment.systemPackages = with pkgs; [
vim
git
curl
];
# Enable Nix daemon
services.nix-daemon.enable = true;
# Nix settings
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
# Binary caches
substituters = [
"https://cache.nixos.org"
"https://nix-community.cachix.org"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
};
# Shell
programs.zsh.enable = true; # default shell on macOS
# Required for nix-darwin
system.stateVersion = 5;
}System Preferences
Dock
{
system.defaults.dock = {
autohide = true;
autohide-delay = 0.0;
autohide-time-modifier = 0.2;
orientation = "bottom"; # left, bottom, right
show-recents = false;
static-only = false; # Show only open apps
tilesize = 48;
mineffect = "scale"; # genie, scale, suck
minimize-to-application = true;
launchanim = false;
mru-spaces = false; # Don't rearrange spaces
};
}Finder
{
system.defaults.finder = {
AppleShowAllExtensions = true;
AppleShowAllFiles = true; # Show hidden files
ShowPathbar = true;
ShowStatusBar = true;
FXDefaultSearchScope = "SCcf"; # Current folder
FXEnableExtensionChangeWarning = false;
FXPreferredViewStyle = "clmv"; # Column view
_FXShowPosixPathInTitle = true;
QuitMenuItem = true; # Allow quitting Finder
};
}Keyboard
{
system.defaults.NSGlobalDomain = {
# Keyboard
KeyRepeat = 2;
InitialKeyRepeat = 15;
ApplePressAndHoldEnabled = false; # Key repeat instead of accents
# Mouse/Trackpad
AppleEnableMouseSwipeNavigateWithScrolls = true;
AppleEnableSwipeNavigateWithScrolls = true;
"com.apple.swipescrolldirection" = true; # Natural scrolling
# UI
AppleInterfaceStyle = "Dark";
AppleShowAllExtensions = true;
NSAutomaticCapitalizationEnabled = false;
NSAutomaticSpellingCorrectionEnabled = false;
NSAutomaticPeriodSubstitutionEnabled = false;
NSAutomaticQuoteSubstitutionEnabled = false;
NSAutomaticDashSubstitutionEnabled = false;
};
}Trackpad
{
system.defaults.trackpad = {
Clicking = true; # Tap to click
TrackpadRightClick = true;
TrackpadThreeFingerDrag = true;
};
}Screenshots
{
system.defaults.screencapture = {
location = "~/Screenshots";
type = "png";
disable-shadow = true;
};
}TouchID for sudo
{
security.pam.services.sudo_local.touchIdAuth = true;
}Homebrew Integration
nix-darwin can manage Homebrew declaratively:
{
homebrew = {
enable = true;
# Uninstall packages not in config
onActivation = {
autoUpdate = true;
cleanup = "zap"; # uninstall + remove caches
upgrade = true;
};
# Taps
taps = [
"homebrew/services"
];
# CLI tools
brews = [
"mas" # Mac App Store CLI
];
# GUI apps
casks = [
"firefox"
"visual-studio-code"
"docker"
"raycast"
"1password"
];
# Mac App Store apps (requires mas)
masApps = {
"Xcode" = 497799835;
"Keynote" = 409183694;
};
};
}Services (launchd)
{
# Built-in services
services.yabai.enable = true; # Tiling WM
services.skhd.enable = true; # Hotkey daemon
services.sketchybar.enable = true;
# Custom launchd service
launchd.user.agents.myservice = {
serviceConfig = {
Label = "com.example.myservice";
Program = "${pkgs.myapp}/bin/myapp";
RunAtLoad = true;
KeepAlive = true;
StandardOutPath = "/tmp/myservice.log";
StandardErrorPath = "/tmp/myservice.err";
};
};
}Fonts
{
fonts.packages = with pkgs; [
(nerdfonts.override { fonts = [ "JetBrainsMono" "FiraCode" ]; })
inter
sf-mono
];
}Environment
{
environment = {
# System-wide packages
systemPackages = with pkgs; [ vim git ];
# Environment variables
variables = {
EDITOR = "vim";
};
# Shell init (all shells)
shellInit = ''
export PATH="$HOME/.local/bin:$PATH"
'';
};
}Home Manager Integration
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-24.11-darwin";
nix-darwin.url = "github:nix-darwin/nix-darwin/nix-darwin-24.11";
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nix-darwin, home-manager, ... }: {
darwinConfigurations."hostname" = nix-darwin.lib.darwinSystem {
system = "aarch64-darwin";
modules = [
./darwin.nix
home-manager.darwinModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home.nix;
}
];
};
};
}Commands
# Rebuild and switch
darwin-rebuild switch --flake .#hostname
# Build without switching
darwin-rebuild build --flake .#hostname
# Check configuration
darwin-rebuild check --flake .#hostname
# Rollback
darwin-rebuild --rollback
# List generations
darwin-rebuild --list-generationsUseful Defaults Commands
# Find preference keys
defaults read > before.txt
# Change setting in System Preferences
defaults read > after.txt
diff before.txt after.txt
# Read specific domain
defaults read com.apple.dock
# Read specific key
defaults read com.apple.dock autohideNix Language Reference
Overview
Nix is a pure, lazy, functional language. Key characteristics:
- Pure: Functions have no side effects
- Lazy: Values computed only when needed
- Functional: Functions are first-class citizens
Basic Types
# Strings
"hello world"
''
Multi-line
string
''
"interpolation: ${pkgs.hello}"
# Numbers
42
3.14
# Booleans
true
false
# Null
null
# Paths (NOT strings)
./relative/path
/absolute/path
~/home/path
# Lists
[ 1 2 3 "mixed" ./types ]
# Attribute Sets
{
key = "value";
nested.key = "works";
"special-key" = "quoted";
}Attribute Sets
# Basic
let
attrs = {
a = 1;
b = 2;
};
in attrs.a # => 1
# Recursive (rec)
rec {
x = 1;
y = x + 1; # Can reference x
}
# Nested access
attrs.nested.deeply.value
attrs.nested.deeply.value or "default" # with fallback
# Has attribute
attrs ? key # => true/false
# Merge
attrs1 // attrs2 # attrs2 overrides attrs1Let Bindings
let
x = 1;
y = 2;
f = a: a + 1;
in
f x + y # => 4Functions
# Single argument
x: x + 1
# Multiple arguments (curried)
x: y: x + y
# Attribute set argument
{ a, b }: a + b
# With defaults
{ a, b ? 0 }: a + b
# With extra attributes (@-pattern)
{ a, b, ... }@args: a + b + args.c
# Calling
(x: x + 1) 5 # => 6
(add 1) 2 # curried
func { a = 1; b = 2; }Control Flow
# If-then-else (expression, not statement!)
if x > 0 then "positive" else "non-positive"
# Assert
assert x > 0; x + 1 # fails if x <= 0
# With (brings attrs into scope)
with pkgs; [ git vim nodejs ]
# equivalent to: [ pkgs.git pkgs.vim pkgs.nodejs ]Inherit
# Shorthand for key = key
let
x = 1;
y = 2;
in {
inherit x y; # same as: x = x; y = y;
}
# From attribute set
{
inherit (pkgs) git vim; # same as: git = pkgs.git; vim = pkgs.vim;
}Import
# Import evaluates a Nix file
import ./file.nix
# Import with arguments
import ./file.nix { inherit pkgs; }
# Import directory (uses default.nix)
import ./directoryBuiltins
# Common builtins
builtins.toString 42 # "42"
builtins.toJSON { a = 1; } # "{\"a\":1}"
builtins.fromJSON "{\"a\":1}" # { a = 1; }
builtins.readFile ./file.txt # file contents
builtins.pathExists ./path # true/false
builtins.attrNames { a=1; b=2; } # [ "a" "b" ]
builtins.attrValues { a=1; b=2; } # [ 1 2 ]
builtins.map (x: x+1) [1 2 3] # [ 2 3 4 ]
builtins.filter (x: x>1) [1 2 3] # [ 2 3 ]
builtins.elem 2 [1 2 3] # true
builtins.length [1 2 3] # 3
builtins.head [1 2 3] # 1
builtins.tail [1 2 3] # [ 2 3 ]
builtins.concatLists [[1] [2]] # [ 1 2 ]
builtins.genList (i: i) 3 # [ 0 1 2 ]
builtins.listToAttrs [{name="a"; value=1;}] # { a = 1; }
builtins.mapAttrs (n: v: v+1) {a=1;} # { a = 2; }
builtins.fetchurl { url = "..."; sha256 = "..."; }
builtins.fetchGit { url = "..."; }
builtins.currentSystem # "x86_64-linux" etc.Lib Functions
Common nixpkgs.lib functions:
{ lib, ... }: {
# Conditionals
lib.mkIf condition { /* config */ }
lib.mkMerge [ config1 config2 ]
# Priority
lib.mkDefault value # priority 1000
lib.mkForce value # priority 50
lib.mkOverride 100 value # custom priority
# List ordering
lib.mkBefore list # prepend
lib.mkAfter list # append
# Options
lib.mkOption { type = lib.types.str; default = ""; }
lib.mkEnableOption "feature"
# Strings
lib.concatStrings [ "a" "b" ] # "ab"
lib.concatStringsSep ", " [ "a" "b" ] # "a, b"
lib.optionalString true "yes" # "yes"
lib.strings.hasPrefix "foo" "foobar" # true
# Lists
lib.optional true "item" # [ "item" ]
lib.optionals true [ 1 2 ] # [ 1 2 ]
lib.flatten [ [1] [2 3] ] # [ 1 2 3 ]
lib.unique [ 1 1 2 ] # [ 1 2 ]
# Attrs
lib.filterAttrs (n: v: v != null) attrs
lib.mapAttrs (n: v: v + 1) attrs
lib.recursiveUpdate attrs1 attrs2
lib.attrByPath ["a" "b"] default attrs
# Paths
lib.makeLibraryPath [ pkgs.openssl ]
lib.makeBinPath [ pkgs.git ]
# System
lib.systems.elaborate "x86_64-linux"
}Learning Resources
- nix.dev - Official learning resource: https://nix.dev
- Tour of Nix - Interactive tutorial: https://nixcloud.io/tour
- Noogle.dev - Function search engine: https://noogle.dev
- Nix Pills - Deep dive series: https://nixos.org/guides/nix-pills
- Nix Reference Manual - Official docs: https://nix.dev/manual/nix
Nixpkgs Advanced Usage
callPackage
callPackage auto-injects dependencies from nixpkgs:
# In your package definition (mypackage.nix)
{ lib, stdenv, fetchFromGitHub, cmake, openssl }:
stdenv.mkDerivation {
pname = "mypackage";
version = "1.0.0";
src = fetchFromGitHub { ... };
nativeBuildInputs = [ cmake ];
buildInputs = [ openssl ];
}
# Calling it
pkgs.callPackage ./mypackage.nix { }
# With overrides
pkgs.callPackage ./mypackage.nix {
openssl = pkgs.openssl_3;
}How callPackage Works
1. Detects function parameters from { lib, stdenv, ... }: 2. Matches parameters against pkgs attributes 3. Injects matched dependencies automatically 4. Second argument allows explicit overrides
Best Practice
Always use callPackage for custom derivations:
- Dependencies are explicit and discoverable
- Easy to override specific inputs
- Follows nixpkgs conventions
override
Modifies function arguments (inputs to the derivation):
# Override specific arguments
pkgs.vim.override {
python3 = pkgs.python311;
}
# Practical examples
pkgs.fcitx5-rime.override {
rimeDataPkgs = [ ./custom-rime-data ];
}
pkgs.vscode.override {
commandLineArgs = "--enable-features=UseOzonePlatform";
}
pkgs.firefox.override {
nativeMessagingHosts = [ pkgs.tridactyl-native ];
}Finding Override Arguments
# In nix repl
nix repl -f '<nixpkgs>'
:e pkgs.vim # Opens in editor
# Or check nixpkgs source on GitHuboverrideAttrs
Modifies derivation attributes (build settings):
# Basic syntax
pkgs.hello.overrideAttrs (oldAttrs: {
patches = oldAttrs.patches or [] ++ [ ./my-patch.patch ];
})
# Access and modify multiple attributes
pkgs.myPackage.overrideAttrs (old: rec {
version = "2.0.0";
src = pkgs.fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v${version}";
sha256 = "sha256-...";
};
})
# Disable tests
pkgs.myPackage.overrideAttrs (old: {
doCheck = false;
})
# Add build flags
pkgs.myPackage.overrideAttrs (old: {
configureFlags = old.configureFlags or [] ++ [ "--enable-feature" ];
})
# Change phases
pkgs.myPackage.overrideAttrs (old: {
postInstall = ''
${old.postInstall or ""}
cp extra-file $out/bin/
'';
})Common Attributes to Override
| Attribute | Purpose |
|---|---|
src | Source code location |
version | Package version |
patches | List of patches |
buildInputs | Runtime dependencies |
nativeBuildInputs | Build-time dependencies |
configureFlags | ./configure arguments |
cmakeFlags | CMake arguments |
doCheck | Run tests |
postInstall | Post-install commands |
Overlays
Overlays globally modify nixpkgs. All dependents use the modified version.
Basic Structure
# Overlay is a function: final -> prev -> { modifications }
(final: prev: {
# prev = original package set
# final = resulting package set (with all overlays applied)
myPackage = prev.myPackage.override { ... };
})Using final vs prev
(final: prev: {
# Use prev to reference original package
vim-modified = prev.vim.override { python = prev.python3; };
# Use final to reference other overlaid packages (avoid infinite recursion)
myApp = prev.callPackage ./app.nix {
someLib = final.myLib; # Uses potentially-overlaid myLib
};
})Applying Overlays in Flakes
# Method 1: In nixosConfiguration
{
nixpkgs.overlays = [
(final: prev: {
myPackage = prev.myPackage.override { ... };
})
# Import from file
(import ./overlays/myoverlay.nix)
];
}
# Method 2: When importing nixpkgs
outputs = { nixpkgs, ... }: let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [
(final: prev: { ... })
];
};
in { ... };Practical Overlay Examples
# Chrome with custom flags
(final: prev: {
google-chrome = prev.google-chrome.override {
commandLineArgs = [
"--proxy-server=127.0.0.1:1080"
"--enable-features=VaapiVideoDecoder"
];
};
})
# Steam with extra packages
(final: prev: {
steam = prev.steam.override {
extraPkgs = pkgs: with pkgs; [ keyutils libkrb5 ];
};
})
# Add custom package
(final: prev: {
myTool = prev.callPackage ./pkgs/mytool { };
})Exporting Overlays from Flakes
outputs = { self, nixpkgs, ... }: {
# Export overlay for others to use
overlays.default = final: prev: {
myPackage = prev.callPackage ./package.nix { };
};
# Use in your own config
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
modules = [{
nixpkgs.overlays = [ self.overlays.default ];
}];
};
};Multiple Nixpkgs Instances
When overlays affect too many packages (cache invalidation), use separate instances:
outputs = { nixpkgs, ... }: let
system = "x86_64-linux";
# Main nixpkgs (clean, uses binary cache)
pkgs = nixpkgs.legacyPackages.${system};
# Custom nixpkgs with overlays (may build from source)
pkgsCustom = import nixpkgs {
inherit system;
overlays = [ myHeavyOverlay ];
};
in {
# Use pkgs for most things
environment.systemPackages = [ pkgs.vim pkgs.git ];
# Use pkgsCustom only where needed
programs.steam.package = pkgsCustom.steam;
};Unfree Packages
Method 1: nixpkgs-unfree (Recommended)
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
nixpkgs-unfree.url = "github:numtide/nixpkgs-unfree";
nixpkgs-unfree.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { nixpkgs-unfree, ... }: {
devShells.default = nixpkgs-unfree.legacyPackages.x86_64-linux.mkShell {
packages = [ nixpkgs-unfree.legacyPackages.x86_64-linux.vscode ];
};
};Method 2: Configuration
# In NixOS/Darwin configuration
{ nixpkgs.config.allowUnfree = true; }
# Or specific packages only
{
nixpkgs.config.allowUnfreePredicate = pkg:
builtins.elem (lib.getName pkg) [
"vscode"
"slack"
"discord"
];
}Method 3: User Config
# ~/.config/nixpkgs/config.nix
{ allowUnfree = true; }Note: nixpkgs.config.allowUnfree in flake.nix does NOT work with nix develop. Use nixpkgs-unfree or user config instead.
Fetchers
# From GitHub
src = pkgs.fetchFromGitHub {
owner = "owner";
repo = "repo";
rev = "v1.0.0"; # tag, branch, or commit
sha256 = ""; # Leave empty first, nix will tell you
};
# From GitLab
src = pkgs.fetchFromGitLab {
owner = "owner";
repo = "repo";
rev = "v1.0.0";
sha256 = "";
};
# From URL
src = pkgs.fetchurl {
url = "https://example.com/file.tar.gz";
sha256 = "";
};
# From Git (with submodules)
src = pkgs.fetchgit {
url = "https://github.com/owner/repo.git";
rev = "abc123";
sha256 = "";
fetchSubmodules = true;
};
# Get hash for fetchurl
# nix-prefetch-url https://example.com/file.tar.gz
# Get hash for fetchFromGitHub
# nix-prefetch-url --unpack https://github.com/owner/repo/archive/v1.0.0.tar.gzFlake Templates
Ready-to-use flake.nix templates for common scenarios.
Minimal NixOS
{
description = "NixOS configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
};
outputs = { self, nixpkgs }: {
nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ ./configuration.nix ];
};
};
}NixOS + Home Manager
{
description = "NixOS with Home Manager";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager, ... }: {
nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home.nix;
}
];
};
};
}nix-darwin (macOS)
{
description = "macOS configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-24.11-darwin";
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, nix-darwin }: {
darwinConfigurations.hostname = nix-darwin.lib.darwinSystem {
system = "aarch64-darwin"; # or x86_64-darwin for Intel
modules = [ ./darwin.nix ];
};
};
}nix-darwin + Home Manager
{
description = "macOS with Home Manager";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-24.11-darwin";
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, nix-darwin, home-manager }: {
darwinConfigurations.hostname = nix-darwin.lib.darwinSystem {
system = "aarch64-darwin";
modules = [
./darwin.nix
home-manager.darwinModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home.nix;
}
];
};
};
}Standalone Home Manager
{
description = "Home Manager standalone";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager }: {
homeConfigurations."username@hostname" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [ ./home.nix ];
};
};
}
# Apply with: home-manager switch --flake .#username@hostnameDevelopment Shell
{
description = "Development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.default = pkgs.mkShell {
packages = with pkgs; [
# Add your dev tools here
nodejs_20
yarn
python3
];
shellHook = ''
echo "Dev environment ready!"
'';
};
});
}Multi-Language Dev Shell
{
description = "Multi-language development";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells = {
default = pkgs.mkShell {
packages = with pkgs; [ git vim ];
};
node = pkgs.mkShell {
packages = with pkgs; [ nodejs_20 yarn pnpm ];
shellHook = ''export PATH="$PWD/node_modules/.bin:$PATH"'';
};
python = pkgs.mkShell {
packages = with pkgs; [ python3 poetry ];
};
rust = pkgs.mkShell {
packages = with pkgs; [ rustc cargo rust-analyzer clippy rustfmt ];
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
};
go = pkgs.mkShell {
packages = with pkgs; [ go gopls gotools ];
};
};
});
}
# Usage:
# nix develop .#node
# nix develop .#python
# nix develop .#rustCross-Platform (NixOS + Darwin)
{
description = "Cross-platform configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
nixpkgs-darwin.url = "github:NixOS/nixpkgs/nixpkgs-24.11-darwin";
home-manager = {
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-24.11";
inputs.nixpkgs.follows = "nixpkgs-darwin";
};
};
outputs = { self, nixpkgs, nixpkgs-darwin, home-manager, nix-darwin, ... }@inputs: {
# NixOS configurations
nixosConfigurations = {
desktop = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./hosts/desktop/configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home/linux.nix;
}
];
specialArgs = { inherit inputs; };
};
};
# macOS configurations
darwinConfigurations = {
macbook = nix-darwin.lib.darwinSystem {
system = "aarch64-darwin";
modules = [
./hosts/macbook/darwin.nix
home-manager.darwinModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.username = import ./home/darwin.nix;
}
];
specialArgs = { inherit inputs; };
};
};
};
}Package + DevShell
{
description = "Package with development shell";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
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 ./package.nix { };
};
devShells.default = pkgs.mkShell {
inputsFrom = [ self.packages.${system}.default ];
packages = with pkgs; [
# Additional dev tools
nixpkgs-fmt
];
};
});
}With Overlays
{
description = "Configuration with overlays";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
};
outputs = { self, nixpkgs }: let
myOverlay = final: prev: {
myPackage = prev.callPackage ./pkgs/mypackage.nix { };
# Modify existing package
vim = prev.vim.override { python3 = final.python311; };
};
in {
# Export overlay for others
overlays.default = myOverlay;
nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
{
nixpkgs.overlays = [ myOverlay ];
}
];
};
};
}Starter configuration.nix
# configuration.nix
{ config, pkgs, ... }: {
imports = [ ./hardware-configuration.nix ];
# Bootloader
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Networking
networking.hostName = "hostname";
networking.networkmanager.enable = true;
# Time zone
time.timeZone = "America/New_York";
# Locale
i18n.defaultLocale = "en_US.UTF-8";
# Users
users.users.username = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
};
# Packages
environment.systemPackages = with pkgs; [
vim git curl wget
];
# Enable flakes
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Allow unfree
nixpkgs.config.allowUnfree = true;
# System version (don't change after install)
system.stateVersion = "24.11";
}Starter home.nix
# home.nix
{ config, pkgs, ... }: {
home.username = "username";
home.homeDirectory = "/home/username"; # /Users/username on macOS
home.packages = with pkgs; [
ripgrep fd jq htop
];
programs.git = {
enable = true;
userName = "Your Name";
userEmail = "you@example.com";
};
programs.zsh = {
enable = true;
autosuggestion.enable = true;
syntaxHighlighting.enable = true;
};
programs.starship.enable = true;
programs.direnv = {
enable = true;
nix-direnv.enable = true;
};
home.stateVersion = "24.11";
}Starter darwin.nix
# darwin.nix
{ config, pkgs, ... }: {
environment.systemPackages = with pkgs; [
vim git curl
];
# Enable Nix daemon
services.nix-daemon.enable = true;
# Flakes
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Zsh (default on macOS)
programs.zsh.enable = true;
# System preferences
system.defaults = {
dock.autohide = true;
finder.AppleShowAllExtensions = true;
NSGlobalDomain.AppleInterfaceStyle = "Dark";
};
# TouchID for sudo
security.pam.services.sudo_local.touchIdAuth = true;
# Homebrew (optional)
homebrew = {
enable = true;
onActivation.cleanup = "zap";
casks = [ "firefox" "visual-studio-code" ];
};
system.stateVersion = 5;
}Related skills
FAQ
How do I avoid duplicate nixpkgs downloads?
Use inputs.nixpkgs.follows to pin dependencies to the parent's nixpkgs.
Why does a flake command ignore my new file?
Untracked files are ignored; git add before running any flake command.