Skip to content
AstroPaper
Go back

🧊 Nix Flakes β€” Development Environment Deep Dive

Updated:
Edit page

🧊 Nix Flakes β€” Development Environment Deep Dive

A progressive guide from Newbie β†’ Expert for setting up reproducible, automated development environments with Nix Flakes.


Table of Contents

Open Table of Contents

1. Why Nix Flakes for Dev Environments?

ProblemNix Flakes Solution
”Works on my machine”Deterministic, hash-locked dependencies via flake.lock
Onboarding takes hours/dayscd project && direnv allow β€” done
Conflicting global tool versionsEach project gets its own isolated toolchain
Broken system after updatesAtomic rollbacks, garbage collection
CI/CD environment driftSame flake.lock = same tools everywhere

Mental Model

flake.nix          β†’ What you WANT (declarative)
flake.lock         β†’ What you GOT (pinned, reproducible)
nix develop        β†’ Activate the environment (imperative entry)
direnv + .envrc    β†’ Activate AUTOMATICALLY on cd (magic ✨)

2. Prerequisites & Installation

Install Nix (multi-user, with flakes)

# Determinate Nix Installer (recommended β€” flakes enabled by default)
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install

# Or official installer + enable flakes manually
sh <(curl -L https://nixos.org/nix/install) --daemon

If using the official installer, enable flakes:

# ~/.config/nix/nix.conf
experimental-features = nix-command flakes

Install direnv + nix-direnv

# Via Nix itself
nix profile install nixpkgs#direnv nixpkgs#nix-direnv

# Hook into your shell (~/.bashrc or ~/.zshrc)
eval "$(direnv hook bash)"   # or zsh

Add to ~/.config/direnv/direnvrc:

source $HOME/.nix-profile/share/nix-direnv/direnvrc

VS Code Extension

Install the direnv extension (mkhl.direnv) so VS Code respects the Nix environment.
Also recommended: Nix IDE (jnoortheen.nix-ide) for syntax highlighting & LSP.


3. Core Concepts

flake.nix Anatomy

{
  description = "My project dev environment";

  # INPUTS β€” dependencies (pinned in flake.lock)
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  };

  # OUTPUTS β€” what this flake provides
  outputs = { self, nixpkgs, ... }:
  let
    # Helper to support multiple systems (x86_64-linux, aarch64-darwin, etc.)
    eachSystem = f:
      nixpkgs.lib.genAttrs
        [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]
        (system: f nixpkgs.legacyPackages.${system});
  in
  {
    devShells = eachSystem (pkgs: {
      default = pkgs.mkShell {
        packages = [ /* tools here */ ];
        shellHook = ''
          echo "πŸš€ Dev environment ready!"
        '';
      };
    });
  };
}

Key Commands

CommandPurpose
nix developEnter the default devShell
nix develop .#backendEnter a named devShell
nix flake updateUpdate all inputs (rewrites flake.lock)
nix flake lock --update-input nixpkgsUpdate only nixpkgs
nix flake showShow what this flake provides
nix flake checkRun checks/tests defined in the flake

4. Progressive Implementation Levels


Level 0 β€” Newbie: Hello Nix Shell

Goal: Understand that Nix can give you tools without installing them globally.

# Try a tool without installing it
nix shell nixpkgs#hello nixpkgs#cowsay
hello
cowsay "I'm using Nix!"
exit  # tools are gone

# Run a one-off command
nix shell nixpkgs#nodejs --command node --version

Key takeaway: Nix provides isolated, temporary environments. Nothing pollutes your system.


Level 1 β€” Junior: Project devShell with flake.nix

Goal: Create a flake.nix that gives your project all the tools it needs.

# flake.nix β€” Minimal Node.js project
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  };

  outputs = { nixpkgs, ... }:
  let
    eachSystem = f:
      nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed
        (system: f nixpkgs.legacyPackages.${system});
  in
  {
    devShells = eachSystem (pkgs: {
      default = pkgs.mkShell {
        packages = with pkgs; [
          nodejs       # Node.js runtime
          pnpm         # Package manager
          gitlint      # Commit message linter
        ];

        shellHook = ''
          echo "πŸ“¦ Node $(node --version) | pnpm $(pnpm --version)"
        '';
      };
    });
  };
}
# Enter the environment
nix develop

# Or with direnv (auto-activate)
echo "use flake" > .envrc
direnv allow

Key takeaway: One file (flake.nix) declares your entire toolchain. Share it via git.


Level 2 β€” Senior: Multi-shell, Tools & Automation

Goal: Multiple dev shells, formatter/linter integration, pre-commit hooks, environment variable management.

# flake.nix β€” Senior level
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    # pre-commit hooks as a flake input
    git-hooks = {
      url = "github:cachix/git-hooks.nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, git-hooks, ... }:
  let
    supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
    eachSystem = f:
      nixpkgs.lib.genAttrs supportedSystems
        (system: f nixpkgs.legacyPackages.${system});
  in
  {
    # ── Pre-commit checks ──────────────────────────────
    checks = eachSystem (pkgs: {
      pre-commit-check = git-hooks.lib.${pkgs.system}.run {
        src = ./.;
        hooks = {
          # Nix
          nixfmt-rfc-style.enable = true;

          # JS/TS
          eslint.enable = true;
          prettier = {
            enable = true;
            types_or = [ "javascript" "typescript" "css" "json" "yaml" "markdown" ];
          };

          # Git
          commitizen.enable = true;

          # General
          check-merge-conflicts.enable = true;
          detect-private-keys.enable = true;
        };
      };
    });

    # ── Dev Shells ─────────────────────────────────────
    devShells = eachSystem (pkgs: {
      # Default β€” full-stack dev environment
      default = pkgs.mkShell {
        inherit (self.checks.${pkgs.system}.pre-commit-check) shellHook;

        packages = with pkgs; [
          # Runtime
          nodejs
          pnpm
          corepack

          # Linting / Formatting
          dprint          # Fast formatter (Rust-based)
          nodePackages.eslint
          nodePackages.prettier

          # Git
          pre-commit
          gitlint
          commitizen

          # AWS
          awscli2
          localstack

          # LSP / Editor
          nodePackages.typescript-language-server

          # Utilities
          jq
          yq-go
          httpie
          hyperfine  # benchmarking
        ];

        env = {
          # AWS LocalStack config
          AWS_ACCESS_KEY_ID = "test";
          AWS_SECRET_ACCESS_KEY = "test";
          AWS_DEFAULT_REGION = "ap-northeast-1";
          AWS_ENDPOINT_URL = "http://localhost:4566";
        };
      };

      # Backend only
      backend = pkgs.mkShell {
        packages = with pkgs; [ nodejs pnpm awscli2 ];
      };

      # Frontend only
      frontend = pkgs.mkShell {
        packages = with pkgs; [ nodejs pnpm ];
      };

      # CI β€” minimal for pipelines
      ci = pkgs.mkShell {
        packages = with pkgs; [ nodejs pnpm ];
      };
    });

    # ── Formatter (nix fmt) ────────────────────────────
    formatter = eachSystem (pkgs: pkgs.nixfmt-rfc-style);
  };
}

.envrc for direnv:

# .envrc
use flake

# Optional: export additional project-specific vars
export BIOME_BINARY=$(which biome 2>/dev/null || true)
# Use the different shells
nix develop           # default (full-stack)
nix develop .#backend
nix develop .#frontend
nix develop .#ci

# Format Nix files
nix fmt

Key takeaway: Named shells for different contexts. Pre-commit hooks declared in Nix. Environment variables set declaratively.


Level 3 β€” Expert: Composable Flake Architecture

Goal: Reusable flake modules, custom overlays, flake-parts, process management, full infrastructure-as-code.

# flake.nix β€” Expert level with flake-parts
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    flake-parts.url = "github:hercules-ci/flake-parts";
    git-hooks = {
      url = "github:cachix/git-hooks.nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    treefmt-nix = {
      url = "github:numtide/treefmt-nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    process-compose-flake.url = "github:Platonic-Systems/process-compose-flake";
    services-flake.url = "github:juspay/services-flake";
  };

  outputs = inputs:
    inputs.flake-parts.lib.mkFlake { inherit inputs; } {
      systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];

      imports = [
        inputs.git-hooks.flakeModule
        inputs.treefmt-nix.flakeModule
        inputs.process-compose-flake.flakeModule
      ];

      perSystem = { config, pkgs, system, ... }: {
        # ── Formatting (treefmt) ───────────────────────
        treefmt = {
          projectRootFile = "flake.nix";
          programs = {
            nixfmt.enable = true;        # Nix
            prettier.enable = true;      # JS/TS/CSS/JSON/YAML/MD
            # dprint.enable = true;      # Alternative: faster
          };
        };

        # ── Pre-commit hooks ──────────────────────────
        pre-commit.settings.hooks = {
          treefmt.enable = true;              # Use treefmt for formatting
          eslint.enable = true;
          commitizen.enable = true;
          check-merge-conflicts.enable = true;
          detect-private-keys.enable = true;
        };

        # ── Process Compose (services) ────────────────
        process-compose."services" = {
          settings.processes = {
            localstack = {
              command = "localstack start";
              readiness_probe.http_get = {
                host = "127.0.0.1";
                port = 4566;
                path = "/_localstack/health";
              };
            };
            backend = {
              command = "cd apps/backend && pnpm dev";
              depends_on.localstack.condition = "process_healthy";
            };
            frontend = {
              command = "cd apps/frontend && pnpm dev";
            };
          };
        };

        # ── Dev Shell ─────────────────────────────────
        devShells.default = pkgs.mkShell {
          inputsFrom = [
            config.pre-commit.devShell       # includes hooks
            config.treefmt.build.devShell     # includes formatters
          ];

          packages = with pkgs; [
            # Runtime
            nodejs
            pnpm
            corepack

            # AWS
            awscli2
            localstack

            # Infrastructure
            terraform
            docker-compose

            # Utilities
            jq yq-go httpie process-compose
          ];

          env = {
            AWS_ACCESS_KEY_ID = "test";
            AWS_SECRET_ACCESS_KEY = "test";
            AWS_DEFAULT_REGION = "ap-northeast-1";
            AWS_ENDPOINT_URL = "http://localhost:4566";
          };

          shellHook = ''
            echo ""
            echo "╔══════════════════════════════════════════════╗"
            echo "β•‘  πŸš€ Dev Environment Ready                   β•‘"
            echo "β•‘  Node: $(node --version)                       β•‘"
            echo "β•‘  pnpm: $(pnpm --version)                       β•‘"
            echo "β•‘                                              β•‘"
            echo "β•‘  Commands:                                   β•‘"
            echo "β•‘    nix run .#services  β€” start all services  β•‘"
            echo "β•‘    nix fmt             β€” format all files    β•‘"
            echo "β•‘    nix flake check     β€” run all checks      β•‘"
            echo "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"
            echo ""
          '';
        };
      };
    };
}

Custom Overlay β€” Pin or Patch a Package

# overlays/default.nix
final: prev: {
  # Pin Node.js to a specific major version
  nodejs = prev.nodejs_22;

  # Override dprint to use a specific version
  # dprint = prev.dprint.overrideAttrs (old: rec {
  #   version = "0.45.0";
  #   src = prev.fetchFromGitHub { ... };
  # });
}
# In flake.nix
outputs = { nixpkgs, ... }: {
  overlays.default = import ./overlays;
  devShells = eachSystem (pkgs:
    let myPkgs = pkgs.extend self.overlays.default;
    in { default = myPkgs.mkShell { /* ... */ }; }
  );
};

Key takeaway: Composable architecture. Services managed via process-compose. Formatters unified via treefmt. Everything is modular and reusable across projects.


5. Project Recipes

Backend β€” NestJS

# flake.nix
{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

  outputs = { nixpkgs, ... }:
  let
    eachSystem = f:
      nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed
        (system: f nixpkgs.legacyPackages.${system});
  in {
    devShells = eachSystem (pkgs: {
      default = pkgs.mkShell {
        packages = with pkgs; [
          nodejs
          pnpm
          # NestJS CLI
          nodePackages."@nestjs/cli"
          # Database
          postgresql
          # AWS
          awscli2
          localstack
          # Quality
          dprint
          pre-commit
          gitlint
          # LSP
          nodePackages.typescript-language-server
        ];

        env = {
          DATABASE_URL = "postgresql://postgres:postgres@localhost:5432/myapp";
          AWS_ACCESS_KEY_ID = "test";
          AWS_SECRET_ACCESS_KEY = "test";
          AWS_DEFAULT_REGION = "ap-northeast-1";
          AWS_ENDPOINT_URL = "http://localhost:4566";
        };

        shellHook = ''
          echo "πŸ—οΈ  NestJS Backend β€” Node $(node --version)"
        '';
      };
    });
  };
}

Frontend β€” React / Next.js

# flake.nix
{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

  outputs = { nixpkgs, ... }:
  let
    eachSystem = f:
      nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed
        (system: f nixpkgs.legacyPackages.${system});
  in {
    devShells = eachSystem (pkgs: {
      default = pkgs.mkShell {
        packages = with pkgs; [
          nodejs
          pnpm
          # Formatting / Linting
          biome
          nodePackages.prettier
          # Image optimization (for Next.js)
          sharp
          # LSP
          nodePackages.typescript-language-server
          # Browsers for testing
          # playwright-driver.browsers
        ];

        env = {
          NEXT_TELEMETRY_DISABLED = "1";
        };

        shellHook = ''
          echo "βš›οΈ  React/Next.js Frontend β€” Node $(node --version)"
          # Ensure biome from Nix is used instead of node_modules
          export BIOME_BINARY="$(command -v biome)"
        '';
      };
    });
  };
}

Full-Stack Monorepo

my-monorepo/
β”œβ”€β”€ flake.nix           ← Root flake with all devShells
β”œβ”€β”€ flake.lock
β”œβ”€β”€ .envrc              ← "use flake"
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ backend/        ← NestJS
β”‚   β”‚   └── .envrc      ← "use flake .#backend" (optional per-app shell)
β”‚   └── frontend/       ← Next.js
β”‚       └── .envrc      ← "use flake .#frontend"
β”œβ”€β”€ packages/           ← Shared libraries
└── infra/              ← Terraform / Pulumi
# Root flake.nix for monorepo
{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

  outputs = { nixpkgs, ... }:
  let
    eachSystem = f:
      nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed
        (system: f nixpkgs.legacyPackages.${system});

    commonPackages = pkgs: with pkgs; [
      nodejs
      pnpm
      dprint
      pre-commit
      gitlint
      jq
    ];
  in {
    devShells = eachSystem (pkgs: {
      # Full monorepo dev
      default = pkgs.mkShell {
        packages = (commonPackages pkgs) ++ (with pkgs; [
          awscli2
          localstack
          docker-compose
          terraform
        ]);
      };

      # Backend only
      backend = pkgs.mkShell {
        packages = (commonPackages pkgs) ++ (with pkgs; [
          awscli2
          localstack
          postgresql
        ]);
        env.DATABASE_URL = "postgresql://postgres:postgres@localhost:5432/myapp";
      };

      # Frontend only
      frontend = pkgs.mkShell {
        packages = (commonPackages pkgs) ++ (with pkgs; [
          biome
        ]);
        env.NEXT_TELEMETRY_DISABLED = "1";
      };

      # CI pipeline (minimal)
      ci = pkgs.mkShell {
        packages = with pkgs; [ nodejs pnpm ];
      };
    });
  };
}

6. Tool Integration

Linting & Formatting

Option A: dprint (Fast, Rust-based)

packages = with pkgs; [ dprint ];
// dprint.json
{
  "plugins": [
    "https://plugins.dprint.dev/typescript-0.93.0.wasm",
    "https://plugins.dprint.dev/json-0.19.4.wasm",
    "https://plugins.dprint.dev/markdown-0.17.8.wasm"
  ]
}

⚠️ Gotcha: pnpm may install its own dprint in node_modules/.bin/. Use shellHook to fix:

shellHook = ''
  # Force node_modules/.bin/dprint to point to Nix's dprint
  NIX_DPRINT_BIN="$(command -v dprint)"
  if [ -n "$NIX_DPRINT_BIN" ]; then
    mkdir -p ./node_modules/.bin
    rm -f ./node_modules/.bin/dprint
    ln -s "$NIX_DPRINT_BIN" ./node_modules/.bin/dprint
    echo "βœ… dprint β†’ $(readlink -f ./node_modules/.bin/dprint)"
  fi
'';

Option B: Biome (All-in-one linter + formatter)

packages = with pkgs; [ biome ];

shellHook = ''
  export BIOME_BINARY="$(command -v biome)"
'';

Option C: treefmt (Unified multi-language formatting)

With treefmt-nix flake input β€” see Level 3 Expert example.

Pre-commit Hooks

Option A: Nix-native with git-hooks.nix (cachix/git-hooks.nix)

See Level 2 Senior example. Hooks are declared in Nix, installed automatically on nix develop.

Option B: Traditional pre-commit + .pre-commit-config.yaml

packages = with pkgs; [ pre-commit gitlint ];

shellHook = ''
  pre-commit install --install-hooks
'';
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-merge-conflict
      - id: detect-private-key

  - repo: https://github.com/commitizen-tools/commitizen
    rev: v3.29.0
    hooks:
      - id: commitizen

  - repo: local
    hooks:
      - id: dprint
        name: dprint format
        entry: dprint fmt --diff
        language: system
        pass_filenames: false

AWS LocalStack

packages = with pkgs; [
  awscli2
  localstack       # LocalStack CLI
  # docker-compose # if running LocalStack via Docker
];

env = {
  AWS_ACCESS_KEY_ID = "test";
  AWS_SECRET_ACCESS_KEY = "test";
  AWS_DEFAULT_REGION = "ap-northeast-1";
  AWS_ENDPOINT_URL = "http://localhost:4566";  # LocalStack default port
};
# Start LocalStack
localstack start -d

# Verify
awslocal s3 mb s3://my-bucket
awslocal s3 ls

# Or use process-compose for orchestration (Expert level)
nix run .#services

Docker / Podman Compose

packages = with pkgs; [
  docker-compose   # or podman-compose
  docker-client    # or podman
];
# docker-compose.yml β€” LocalStack + PostgreSQL
services:
  localstack:
    image: localstack/localstack
    ports:
      - "4566:4566"
    environment:
      - SERVICES=s3,sqs,lambda,dynamodb
      - DEFAULT_REGION=ap-northeast-1

  postgres:
    image: postgres:16
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: myapp

7. Auto-Setup: direnv + nix-direnv

The killer feature: cd into your project β†’ environment is ready. No manual steps.

How It Works

You:     cd ~/projects/my-app
direnv:  Loading .envrc β†’ "use flake"
nix:     Activating devShell from flake.nix
Result:  All tools available. ENV vars set. Hooks installed.
         It just works. ✨

Setup

  1. Install direnv + nix-direnv (see Prerequisites)

  2. Create .envrc in your project root:

# .envrc β€” Basic
use flake

# .envrc β€” With extras
use flake

# Project-specific overrides
export BIOME_BINARY=$(which biome 2>/dev/null || true)
export NODE_ENV=development

# Watch additional files for changes
watch_file flake.nix
watch_file flake.lock
  1. Allow direnv:
direnv allow
  1. Add to .gitignore:
# direnv
.direnv/

Per-directory Shells in Monorepo

# apps/backend/.envrc
source_up        # inherit parent .envrc
use flake ..#backend

# apps/frontend/.envrc
source_up
use flake ..#frontend

VS Code Integration

Install mkhl.direnv extension. It will:


8. Developer Experience Enhancers

starship β€” Beautiful Shell Prompt

Shows Nix shell indicator, git branch, Node version, etc.

packages = with pkgs; [ starship ];

shellHook = ''
  eval "$(starship init bash)"  # or zsh
'';

process-compose β€” Service Orchestration

Run backend, frontend, database, LocalStack all at once:

nix run .#services
# Opens a TUI showing all processes, logs, health status

devenv β€” Higher-level Nix Dev Environments

An alternative/complementary approach with more batteries included:

# devenv.nix
{ pkgs, ... }: {
  languages.javascript = {
    enable = true;
    package = pkgs.nodejs;
    pnpm.enable = true;
  };

  services.postgres.enable = true;
  services.localstack.enable = true;

  pre-commit.hooks = {
    prettier.enable = true;
    eslint.enable = true;
  };
}

mise (formerly rtx) β€” Polyglot Version Manager

Can coexist with Nix for teams partially adopting Nix:

packages = with pkgs; [ mise ];

Cachix β€” Binary Cache

Avoid rebuilding tools from source:

# Setup
nix profile install nixpkgs#cachix
cachix use my-org

# In CI, push built artifacts
cachix push my-org ./result

9. Cheatsheet

Flake Commands

# ── Environment ───────────────────────────────────
nix develop                   # Enter default devShell
nix develop .#backend         # Enter named devShell
nix develop --command zsh     # Use a specific shell

# ── Flake Management ─────────────────────────────
nix flake init                # Create a new flake.nix
nix flake update              # Update all inputs (rewrite flake.lock)
nix flake lock --update-input nixpkgs  # Update single input
nix flake show                # Show flake outputs
nix flake check               # Run checks (tests, pre-commit, etc.)
nix flake metadata            # Show flake metadata & inputs tree

# ── Formatting ────────────────────────────────────
nix fmt                       # Format with declared formatter

# ── Building & Running ───────────────────────────
nix build                     # Build default package
nix build .#myPackage         # Build named package
nix run .#services            # Run a named app

# ── Debugging ─────────────────────────────────────
nix repl                      # Interactive Nix REPL
# In REPL:
#   :lf .                     # Load current flake
#   outputs.<TAB>             # Explore outputs
#   :q                        # Quit

# ── Garbage Collection ───────────────────────────
nix store gc                  # Remove unused store paths
nix store gc --min 10G        # Free at least 10 GB

# ── Search ────────────────────────────────────────
nix search nixpkgs nodejs     # Find packages
nix search nixpkgs --json nodejs | jq  # JSON output

direnv Commands

direnv allow                  # Trust current .envrc
direnv deny                   # Untrust current .envrc
direnv reload                 # Force reload environment
direnv status                 # Show direnv state

Common Patterns

# Pin Node.js version
nodejs_22

# Override yarn to use specific Node
(yarn.override { nodejs = nodejs_22; })

# Python with packages
(python3.withPackages (ps: [ ps.flask ps.boto3 ]))

# Use env attribute for clean environment variable declaration
env = {
  MY_VAR = "value";
  DATABASE_URL = "postgresql://localhost/mydb";
};

# Multiple inputs with follows (dedup nixpkgs)
inputs.some-tool = {
  url = "github:owner/tool";
  inputs.nixpkgs.follows = "nixpkgs";
};

10. Troubleshooting

ProblemSolution
error: experimental feature 'flakes' is disabledAdd experimental-features = nix-command flakes to ~/.config/nix/nix.conf
pnpm uses wrong binary from node_modules/.bin/Use shellHook to symlink Nix binary (see dprint gotcha)
direnv not loading in VS CodeInstall mkhl.direnv extension. Restart VS Code after direnv allow
nix develop is slow first timeExpected β€” it downloads/builds packages. Subsequent runs use cache. Use Cachix for team-wide binary cache
warning: Git tree is dirtyCommit or git add your flake.nix. Nix tracks git-tracked files only
error: path '/nix/store/...' is not validRun nix store repair --all or nix store gc
Environment not updating after flake.nix changedirenv reload or exit and re-run nix develop
Collision between Nix tool and node_modules toolUse shellHook to create symlinks or export TOOL_BINARY=$(which tool)

11. Open Questions & Brainstorm

Architecture & Strategy

Tooling & DX

Infrastructure

Team & Adoption

Performance & Maintenance

Advanced Exploration


References


Edit page
Share this post:

Previous Post
Monorepo Architecture β€” Complete Engineering Guide
Next Post
RFCs (Request for Comments)