NI

nix-for-dev

Configures zero-input Nix flakes for fast development setups.

Install

mkdir -p .claude/skills/nix-for-dev && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14683" && unzip -o skill.zip -d .claude/skills/nix-for-dev && rm skill.zip

Installs to .claude/skills/nix-for-dev

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use this when setting up Nix for a development project (devShell + package build) and you care about `nix develop` being fast. Covers the zero-inputs flake.nix + npins + default.nix/shell.nix layout, sub-flakes for non-user-facing Nix, and language-specific recommendations.
274 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Configure `flake.nix` with zero inputs for fast `nix develop`
  • Pin sources with `npins` and import via `fetchTarball` / `callPackage`
  • Define leaf packages via an overlay
  • Share environment variables between build and devShell
  • Expose extra shells via `overrideAttrs` for specific use cases

How it works

The skill configures `flake.nix` with zero inputs to minimize cold evaluation time. It uses `npins` to manage pinned sources and imports them, defining leaf packages via an overlay and sharing environment variables between the build and devShell.

Inputs & outputs

You give it
A request to set up a Nix development environment for a project, e.g., "Initialize a Nix dev environment for my project."
You get back
A `flake.nix` and associated Nix files configured for fast `nix develop` with pinned sources and a clean structure.

When to use nix-for-dev

  • Initialize a Nix dev environment
  • Optimize Nix evaluation time
  • Set up project-local package builds

About this skill

Nix for development

A low-overhead Nix setup for dev projects: ~1s cold nix develop, ~0.1s warm, with a clean separation between the user-facing flake and internal Nix. Reference implementation: juspay/kolu.

Core principle: zero flake inputs

The top-level flake.nix declares no inputs at all. Each flake input adds ~1.5s of fetcher-cache verification on cold eval; a single nixpkgs input costs ~7s. With zero inputs, cold nix develop is ~1.0s, warm ~0.1s.

Instead, pin sources with npins and import them via fetchTarball / callPackage from files under nix/.

# flake.nix — slim, zero inputs
{
  outputs = { self, ... }:
    let
      systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ];
      eachSystem = f: builtins.listToAttrs (map
        (system: {
          name = system;
          value = f (import ./nix/nixpkgs.nix { inherit system; });
        })
        systems);
    in
    {
      packages = eachSystem (pkgs: {
        default = import ./default.nix { inherit pkgs; };
      });
      devShells = eachSystem (pkgs: {
        default = import ./shell.nix { inherit pkgs; };
      });
    };
}

Do not add nixpkgs, flake-parts, git-hooks, etc. as flake inputs. If a downstream consumer needs to override the pinned nixpkgs, they can override the npins source (NPINS_OVERRIDE_nixpkgs=/path).

File layout

flake.nix          # slim wrapper, zero inputs
default.nix        # main package(s), pkgs ? import ./nix/nixpkgs.nix { }
shell.nix          # devShell,        pkgs ? import ./nix/nixpkgs.nix { }
nix/
  nixpkgs.nix      # imports npins source + applies overlay
  overlay.nix      # injects leaf packages into pkgs
  env.nix          # env vars shared by build + devShell + wrapper
  packages/<name>/ # callPackage-style leaf packages
npins/
  default.nix      # generated by npins (do not edit)
  sources.json     # pinned source revisions

default.nix and shell.nix both accept pkgs ? import ./nix/nixpkgs.nix { } so they also work via plain nix-build / nix-shell, not just nix develop.

npins workflow

npins init                                   # creates npins/ and pins nixpkgs
npins add github nixos nixpkgs --branch nixpkgs-unstable
npins update                                 # update all pins
npins update nixpkgs                         # update one

nix/nixpkgs.nix:

# Pinned nixpkgs import — managed by npins.
# To update: npins update nixpkgs
let
  sources = import ../npins;
  nixpkgs = import sources.nixpkgs;
in
args: nixpkgs (args // {
  overlays = (args.overlays or [ ]) ++ [ (import ./overlay.nix) ];
})

Leaf packages via overlay

Pure callPackage-style packages live in nix/packages/<name>/default.nix and are auto-injected via nix/overlay.nix:

# nix/overlay.nix
final: _prev: {
  my-fonts = final.callPackage ./packages/fonts { };
}

Packages that need per-invocation arguments (commit hash, build-time env) stay in the top-level default.nix — overlays are for things that legitimately belong on pkgs.

Shared env vars

Define a single nix/env.nix returning an attrset; both the build derivation and the devShell spread it into their env. This prevents drift between nix build and nix develop.

# nix/env.nix
{ pkgs }: {
  MY_FONTS_DIR = pkgs.my-fonts;
  MY_GH_BIN    = "${pkgs.gh}/bin/gh";
}

devShell conventions

  • Use pkgs.mkShell directly. Do not introduce flake-parts to "structure" it.
  • Use pkgs.writeShellApplication (not writeShellScriptBin) — strict mode + runtimeInputs validation. Always set meta.description.
  • Run nixpkgs-fmt from the devShell rather than wiring up formatter perSystem.
  • Expose extra shells via overrideAttrs so the base stays fast:
devShells = eachSystem (pkgs:
  let default = import ./shell.nix { inherit pkgs; };
  in {
    inherit default;
    e2e = default.overrideAttrs (prev: {
      env = (prev.env or { }) // {
        PLAYWRIGHT_BROWSERS_PATH = pkgs.playwright-driver.browsers;
      };
    });
  });

nix develop .#e2e for the heavier shell; the default stays cold-start-fast.

Sub-flakes for non-user-facing Nix

Module integration tests (home-manager, NixOS, Darwin) genuinely need flake-parts-style inputs (home-manager, nix-darwin). Keep those inputs out of the top-level flake by nesting a sub-flake under nix/<name>/flake.nix. CI builds it with --override-input pointing back at the parent:

# nix/home/example/flake.nix
{
  inputs = {
    self_pkg.url     = "github:owner/repo";   # parent; CI passes --override-input
    nixpkgs.url      = "github:nixos/nixpkgs/nixpkgs-unstable";
    home-manager.url = "github:nix-community/home-manager";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";
  };
  outputs = { nixpkgs, home-manager, self_pkg, ... }: {
    # nixosConfigurations / checks that exercise self_pkg.homeManagerModules.default
  };
}

Users running nix develop / nix run on the top-level flake never evaluate this graph. Only CI does.

Language templates

Haskell

Use haskell-flake via its standalone entry point lib.evalHaskellProject (not the flake-parts module). See haskell.nixos.asia/standalone for the full API.

In keeping with the zero-inputs principle, pin haskell-flake via npins (npins add github srid haskell-flake) and call it from default.nix:

# default.nix
{ pkgs ? import ./nix/nixpkgs.nix { } }:
let
  sources = import ./npins;
  haskell-flake = import sources.haskell-flake;
  project = (haskell-flake.lib { inherit pkgs; }).evalHaskellProject {
    projectRoot = ./.;
    modules = [{
      settings.mypackage.haddock = false;
      devShell.tools = hp: { inherit (hp) fourmolu; };
    }];
  };
in
project.packages.mypackage.package

Wire project.devShell into shell.nix the same way. If you need flake-parts and nixos-unified autowiring (multi-package projects, fully wired checks), see the nix-haskell skill — it uses haskell-template and trades startup time for ergonomics.

TypeScript / pnpm

See nix-typescript for fetchPnpmDeps and hash management.

Dev services

For multi-process dev environments (server + watcher + db), use process-compose-flake and services-flake via their standalone entry points (no flake-parts needed). Both flakes have zero inputs themselves, so pinning them via npins keeps the top-level flake.nix zero-input too:

  • process-compose-flake exposes lib.evalModules / lib.makeProcessCompose for module evaluation outside flake-parts.
  • services-flake exposes processComposeModules.default (a path) — pass it as a module to evalModules.
# shell.nix
{ pkgs ? import ./nix/nixpkgs.nix { } }:
let
  sources = import ./npins;
  pcLib = import "${sources.process-compose-flake}/nix/lib.nix" { inherit pkgs; };
  servicesMod = pcLib.evalModules {
    modules = [
      "${sources.services-flake}/nix/process-compose"
      { services.redis."r1".enable = true; }
    ];
  };
in
pkgs.mkShell {
  inputsFrom = [ servicesMod.config.services.outputs.devShell ];
}

Reference: services-flake/example/without-flake-parts and doc/without-flake-parts.md.

Companion docs

  • nix-perf — diagnosing slow nix develop / nix flake archive
  • nix-justfile — justfile recipe conventions for Nix projects
  • nix-typescript — pnpm + Nix conventions
  • nix-haskell — flake-parts + haskell-template variant (when ergonomics > cold start)
  • juspay/kolu — full reference implementation

When not to use it

  • When a downstream consumer needs to override the pinned nixpkgs directly as a flake input
  • When the project requires `flake-parts` for module integration tests at the top-level
  • When the user prioritizes ergonomics over cold start time for `nix develop`

Prerequisites

`npins`

Limitations

  • The top-level `flake.nix` declares no `inputs`.
  • It avoids `flake-parts` for structuring `devShells` directly.
  • The skill focuses on a specific file layout and `npins` workflow.

How it compares

This skill provides a specific, low-overhead Nix setup focused on fast `nix develop` performance by avoiding flake inputs and using `npins`, offering a more optimized approach than typical Nix flake configurations.

Compared to similar skills

nix-for-dev side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
nix-for-dev (this skill)02moReviewAdvanced
storage-networking67moReviewAdvanced
kubernetes-architect64moNo flagsAdvanced
senior-devops78moReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

storage-networking

pluginagentmarketplace

Master Kubernetes storage management and networking architecture. Learn persistent storage, network policies, service discovery, and ingress routing.

663

kubernetes-architect

sickn33

Expert Kubernetes architect specializing in cloud-native infrastructure, advanced GitOps workflows (ArgoCD/Flux), and enterprise container orchestration. Masters EKS/AKS/GKE, service mesh (Istio/Linkerd), progressive delivery, multi-tenancy, and platform engineering. Handles security, observability, cost optimization, and developer experience. Use PROACTIVELY for K8s architecture, GitOps implementation, or cloud-native platform design.

636

senior-devops

davila7

Comprehensive DevOps skill for CI/CD, infrastructure automation, containerization, and cloud platforms (AWS, GCP, Azure). Includes pipeline setup, infrastructure as code, deployment automation, and monitoring. Use when setting up pipelines, deploying applications, managing infrastructure, implementing monitoring, or optimizing deployment processes.

720

gitops-workflow

sickn33

Implement GitOps workflows with ArgoCD and Flux for automated, declarative Kubernetes deployments with continuous reconciliation. Use when implementing GitOps practices, automating Kubernetes deployments, or setting up declarative infrastructure management.

521

k8s-manifest-generator

wshobson

Create production-ready Kubernetes manifests for Deployments, Services, ConfigMaps, and Secrets following best practices and security standards. Use when generating Kubernetes YAML manifests, creating K8s resources, or implementing production-grade Kubernetes configurations.

319

server-management

davila7

Server management principles and decision-making. Process management, monitoring strategy, and scaling decisions. Teaches thinking, not commands.

113

Search skills

Search the agent skills registry