MCPInstall

nix-best-practices

Use when working with Nix flakes, overlays, shell.nix, or flake.nix files.

Installs
547
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/alleneubank/claude-code --skill nix-best-practices
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads nix-best-practices automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

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 { ... };
      })
    ];
  };
in

Handling 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"
    ];
  };
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 (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.0

Dev 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 flake

For unfree packages without nixpkgs-unfree:

export NIXPKGS_ALLOW_UNFREE=1
use flake --impure

Common 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 .#packageName

Troubleshooting

"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.

FAQ

What does the nix-best-practices skill do?

Use when working with Nix flakes, overlays, shell.nix, or flake.nix files.

How do I install nix-best-practices?

Run npx skills add https://github.com/alleneubank/claude-code --skill nix-best-practices in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/nix-best-practices/ (personal) or .claude/skills/nix-best-practices/ (project).

Which AI agents can use nix-best-practices?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is nix-best-practices safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains nix-best-practices?

It is published by alleneubank in alleneubank/claude-code. It has 547 recorded installs.

More from alleneubank/claude-code

View all →

typescript-best-practices

alleneubank/claude-code

Use when reading or writing TypeScript or JavaScript files (.ts, .tsx, .js, tsconfig.json).

react-best-practices

alleneubank/claude-code

Use when reading or writing React components (.tsx, .jsx files with React imports).

Frontend & Design3.2K installs

python-best-practices

alleneubank/claude-code

Use when reading or writing Python files (.py, pyproject.toml, requirements.txt).

web-fetch

alleneubank/claude-code

Fetches web content as clean markdown by preferring markdown-native responses and falling back to selector-based HTML extraction. Use for documentation, articles, and reference pages at http/https URLs.

Docs & Writing978 installs

zig-best-practices

alleneubank/claude-code

Use when reading or writing Zig files (.zig, build.zig, build.zig.zon).

playwright-best-practices

alleneubank/claude-code

Open the skill to read its SKILL.md instructions.

Testing & QA373 installs

Popular code quality & workflow skills

View category →

grill-me

mattpocock/skills

A relentless interview to sharpen a plan or design.

Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.

handoff

mattpocock/skills

Compact the current conversation into a handoff document for another agent to pick up.

domain-modeling

mattpocock/skills

Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.

codebase-design

mattpocock/skills

Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.

diagnosing-bugs

mattpocock/skills

Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.