Development Guides
Learn how to extend and customize your Nix configuration.
Overview
Section titled “Overview”This section covers development workflows for customizing your Nix configuration. Learn to add packages, create modules, build profiles, and test changes before applying them.
Documentation in This Section
Section titled “Documentation in This Section”Start here to install new software.
Covers:
- Finding packages in nixpkgs
- Adding system-wide packages
- Adding user packages with Home Manager
- Installing Homebrew casks
- Custom package creation
Perfect for: Adding software to your system
Write custom Nix modules for reusable configuration.
Covers:
- Module structure and basics
- Defining module options
- Conditional configuration
- Platform-specific modules
- Testing and validation
Perfect for: Building reusable configuration components
Build composable feature bundles.
Covers:
- Profile vs module differences
- Creating simple profiles
- Role-based profiles (devops, security, ai-ml)
- Language-specific profiles
- Profile composition
Perfect for: Bundling related tools and configurations
Customize and modify packages using overlays.
Covers:
- Overlay fundamentals
- Overriding package versions
- Applying patches
- Adding custom packages
- Python and language-specific overlays
Perfect for: Customizing existing packages
Test changes before applying to your system.
Covers:
- Dry run builds
- Diff analysis
- Syntax validation
- CI/CD integration
- Troubleshooting builds
Perfect for: Safe system updates and development
Quick Start
Section titled “Quick Start”Add a Package (2 minutes)
Section titled “Add a Package (2 minutes)”# 1. Find packagenix search nixpkgs htop
# 2. Add to config# In hosts/your-mac.nix:environment.systemPackages = [ pkgs.htop ];
# 3. Applydarwin-rebuild switch --flake .#your-hostname
Create a Profile (5 minutes)
Section titled “Create a Profile (5 minutes)”# 1. Create profile filecat > nix/profiles/my-tools.nix << 'EOF'{ config, pkgs, ... }:{ environment.systemPackages = with pkgs; [ htop ripgrep fd ];}EOF
# 2. Add to flake.nix importsimports = [ ./nix/profiles/my-tools.nix ];
# 3. Build and applydarwin-rebuild switch --flake .#your-hostname
Test Before Applying
Section titled “Test Before Applying”# Build without applyingdarwin-rebuild build --flake .#your-hostname
# Check what changednix store diff-closures /run/current-system ./result
# Apply if gooddarwin-rebuild switch --flake .#your-hostname
Development Workflows
Section titled “Development Workflows”Package Development
Section titled “Package Development”1. Search/create package ↓2. Test build locally ↓3. Add to system/user config ↓4. Test build (dry run) ↓5. Apply changes
Module Development
Section titled “Module Development”1. Create module with options ↓2. Validate syntax ↓3. Test evaluation ↓4. Build system with module ↓5. Apply and verify
Profile Development
Section titled “Profile Development”1. Identify related tools ↓2. Create profile file ↓3. Import in flake ↓4. Test build ↓5. Apply changes
Common Tasks
Section titled “Common Tasks”Install Software
Section titled “Install Software”Quick:
environment.systemPackages = [ pkgs.package-name ];
Create Feature Bundle
Section titled “Create Feature Bundle”Quick:
{ pkgs, ... }: { environment.systemPackages = with pkgs; [ tool1 tool2 ];}
Customize Package
Section titled “Customize Package”Quick:
nixpkgs.overlays = [(final: prev: { mypackage = prev.mypackage.override { enableFeature = true; };})];
Write Configuration Module
Section titled “Write Configuration Module”Quick:
{ config, lib, ... }: { options.mymodule.enable = lib.mkEnableOption "my module"; config = lib.mkIf config.mymodule.enable { };}
Test Changes
Section titled “Test Changes”Quick:
darwin-rebuild build --flake .#hostnamenix store diff-closures /run/current-system ./result
Architecture
Section titled “Architecture”┌─────────────────────────────────────────────────────────────┐│ Development Layers │├─────────────────────────────────────────────────────────────┤│ ││ flake.nix ││ │ ││ ├─> Modules (nix/modules/) ││ │ └─> Common, Darwin, Linux, Cloud ││ │ ││ ├─> Profiles (nix/profiles/) ││ │ └─> Cloud-CLI, Developer, Hardware-Security ││ │ ││ ├─> Overlays (nix/overlays/) ││ │ └─> Package customizations ││ │ ││ ├─> Packages (nix/packages/) ││ │ └─> Custom packages ││ │ ││ └─> Hosts (hosts/) ││ └─> Machine-specific configs ││ │└─────────────────────────────────────────────────────────────┘
Best Practices
Section titled “Best Practices”1. Test Before Applying
Section titled “1. Test Before Applying”# Always test firstdarwin-rebuild build --flake .#hostnamenix store diff-closures /run/current-system ./resultdarwin-rebuild switch --flake .#hostname
2. Use Version Control
Section titled “2. Use Version Control”# Commit before major changesgit add .git commit -m "Add new feature"# Test# If breaks: git reset --hard HEAD
3. Organize by Purpose
Section titled “3. Organize by Purpose”nix/├── modules/ # Low-level, configurable├── profiles/ # High-level, opinionated├── overlays/ # Package modifications└── packages/ # Custom packages
4. Document Changes
Section titled “4. Document Changes”# Good: Explain why# Use Node 20 for compatibility with tool Xnodejs = prev.nodejs_20;
# Bad: No contextnodejs = prev.nodejs_20;
5. Keep It Simple
Section titled “5. Keep It Simple”# ✅ Good: Clear and simpleenvironment.systemPackages = [ pkgs.htop ];
# ❌ Bad: Over-engineeredenvironment.systemPackages = lib.optionals (config.enable.monitoring) [ (if config.advanced then pkgs.htop-advanced else pkgs.htop) ];
Troubleshooting
Section titled “Troubleshooting”Build Fails
Section titled “Build Fails”# Show full errornix build .#package --show-trace
# Keep failed build for inspectionnix build .#package --keep-failed
Package Not Found
Section titled “Package Not Found”# Update flakenix flake update
# Search againnix search nixpkgs package-name
Module Error
Section titled “Module Error”# Check module loadsnix eval .#darwinConfigurations.hostname.config.mymodule.enable
# Validate syntaxnix-instantiate --parse nix/modules/mymodule.nix
See Troubleshooting Guide for more.
Examples
Section titled “Examples”Example 1: Python Development Setup
Section titled “Example 1: Python Development Setup”{ pkgs, ... }:{ environment.systemPackages = with pkgs; [ python3 python3Packages.pip python3Packages.virtualenv poetry black ruff ];
environment.variables = { PYTHONPATH = "$HOME/.local/lib/python3.11/site-packages"; };}
Example 2: Cloud Developer Profile
Section titled “Example 2: Cloud Developer Profile”{ pkgs, ... }:{ environment.systemPackages = with pkgs; [ # Cloud CLIs awscli2 google-cloud-sdk azure-cli
# Container tools docker kubectl helm
# IaC terraform pulumi ];}
Example 3: Custom Package
Section titled “Example 3: Custom Package”{ pkgs, lib }:
pkgs.buildGoModule { pname = "my-tool"; version = "1.0.0";
src = pkgs.fetchFromGitHub { owner = "user"; repo = "my-tool"; rev = "v1.0.0"; sha256 = "sha256-..."; };
vendorHash = "sha256-...";
meta = { description = "My custom tool"; license = lib.licenses.mit; };}
Learning Path
Section titled “Learning Path”Beginner
Section titled “Beginner”- Adding Packages - Start here
- Testing Builds - Learn safe updates
- Creating Profiles - Bundle tools
Intermediate
Section titled “Intermediate”- Creating Modules - Build reusable config
- Working with Overlays - Customize packages
- Examples - Study real-world examples
Advanced
Section titled “Advanced”- Design Philosophy - Understand architecture
- Structure Guide - Deep dive
- Nix Fundamentals - Master Nix
Related Documentation
Section titled “Related Documentation”In This Section
Section titled “In This Section”Other Sections
Section titled “Other Sections”- Examples - Practical walkthroughs
- Reference - Technical references
- Architecture - Design docs
External Resources
Section titled “External Resources”- Nixpkgs Manual - Package docs
- Nix Pills - In-depth tutorials
- NixOS Wiki - Community docs
- Nix Dev - Learning resources
Ready to develop? Start with Adding Packages!