Configure a nix flake development shell

#nix#nix flakes

14 March 2025 | Updated on 25 March 2025

Nix is a tool that allows you to make reproducible, declarative and reliable development environments.

You can define nix development environments via nix flakes.

Nix flakes allow really advanced workflows. This article only describe how to create a basic flake with a dev shell.

Prerequisites

Create a flake

  • flakes are defined in flake.nix files
  • these are usually placed in the root of your git repo project
  • the following flake defines
    • a dev shell for linux and mac computers
    • and installs two cli tools (eza, bat)
flake.nix
{
  description = "nix flake dev shell";

  # Nixpkgs / NixOS version to use.
  inputs.nixpkgs.url = "nixpkgs/nixos-24.11";

  outputs = { self, nixpkgs }:
    let

      # System types to support.
      supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];

      # Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
      forAllSystems = nixpkgs.lib.genAttrs supportedSystems;

      # Nixpkgs instantiated for supported system types.
      nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });

    in
    {

      # Add dependencies that are only needed for development
      devShells = forAllSystems (system:
        let
          pkgs = nixpkgsFor.${system};
        in
        {
          default = pkgs.mkShell {
            buildInputs = with pkgs; [
              # search packages: https://search.nixos.org/packages
              # add packages here

              # 2 examples:

              # eza (a modern replacement for ls)
              eza

              # bat (cat clone with syntax highlighting)
              bat
            ];
          };
        });

    };
}

Check flake file

  • it is important that you add/stage the flake.nix file to your git repo

    • otherwise the following command will not work
    • but you only need to stage the file. a commit is not required yet
  • then you can check if the flake.nix is valid via:

    nix flake check --all-systems
  • If you haven’t used the defined dependencies before, Nix will download everything automatically.

  • Nix will also generate a flake.lock file. You will never edit this file manually.

Commit flake to git

  • if you are happy with your flake configuration you should commit both files to git (flake.nix and flake.lock)

How to change flake later

  • you can change the flake.nix file in the future (e. g. add/remove packages)
  • after editing the file you should run the following command to update the lock file
    nix flake update
  • commit both files again

Use dev shell

Open nix development shell

  • use a basic dev shell
    nix develop
  • use a zsh dev shell
    nix develop --command zsh

Run commands

  • you can use the shell like usual
  • your defined packages will be available
    which eza
    • the binary path will be some symlink to nix store

Exit dev shell

  • exit a nix development shell like usual via exit or ctrl + d

Use flake with vscode

  • there are vscode extensions to activate nix flakes from inside vscode
  • but in my opinion the best approach is to start vscode an already activated nix flake development shell
  • this way all vscode processes will use the nix environment

Start vscode in nix shell

via two commands

nix develop

# ... then in nix shell:
code .

via one command

nix develop --command code .

Ensure correct environment paths

macos

  • fixing environment variable path problem in zsh macos
    • especially when opening code . in nix develop shell
  • without the following settings vscode will change the user path variable and the nix development path would be incorrect
    • then tools/binaries from the nix flake do not get the highest priority
  • so you should add these settings in your vscode user (or workspace) settings to ensure correct behaviour
settings.json
{
  "terminal.integrated.defaultProfile.osx": "zsh",
  // this does NOT work:
  // "terminal.integrated.inheritEnv": false, 
  // but this does work:
  "terminal.integrated.profiles.osx": {
    "zsh": {
      "path": "zsh",
      "args": [], // just set this empty
      // do not use this!
      // "env": {
      //   "PATH": "${env:PATH}" 
      // }
      }
    }
}

Example nix flakes

Additional resources


Related content