Chapter 07

Flakes——现代 Nix

Flakes 解决了 Nixpkgs 频道不可复现的根本问题,是现代 Nix 项目管理的标准

为什么需要 Flakes?

在 Flakes 出现之前,Nix 依赖"频道"(channel)来获取 Nixpkgs。频道的问题是:

Flakes 的核心是:用 flake.lock 文件将所有依赖(包括 nixpkgs)精确锁定到特定的 Git commit,就像 Node.js 的 package-lock.json 一样。

启用 Flakes

# 方法1:NixOS configuration.nix 中启用
{
  nix.settings.experimental-features = [ "nix-command" "flakes" ];
}

# 方法2:非 NixOS 系统,编辑 ~/.config/nix/nix.conf
# experimental-features = nix-command flakes

# 验证(Determinate Installer 默认已启用)
nix --version       # 查看版本
nix flake --help    # 如果能看到 flake 子命令,说明已启用

flake.nix 结构

每个 flake 是一个目录,包含 flake.nix 文件(通常也有 flake.lock)。

# flake.nix 完整结构
{
  # 可选:描述这个 flake 的用途
  description = "My awesome project";

  # ── inputs:声明依赖的其他 flake ──────────
  inputs = {
    # Nixpkgs(最常用)
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

    # 稳定版 nixpkgs(同时引用两个版本)
    nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-24.05";

    # 其他工具
    flake-utils.url = "github:numtide/flake-utils";
    home-manager = {
      url = "github:nix-community/home-manager";
      # 让 home-manager 使用与主 flake 相同的 nixpkgs
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  # ── outputs:这个 flake 提供的产物 ────────
  # 接收所有 inputs 作为参数
  outputs = { self, nixpkgs, flake-utils, home-manager, ... }:
    # flake-utils 帮助处理多平台(x86_64-linux、aarch64-darwin 等)
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in {

        # 可构建的包(nix build .#my-app)
        packages.my-app = pkgs.stdenv.mkDerivation { ... };
        packages.default = self.packages.${system}.my-app;

        # 开发环境(nix develop)
        devShells.default = pkgs.mkShell {
          buildInputs = with pkgs; [ nodejs_20 git ];
        };

        # 可运行的应用(nix run .#my-app)
        apps.my-app = flake-utils.lib.mkApp {
          drv = self.packages.${system}.my-app;
        };

      }
    ) // {
      # 系统级配置(不依赖 system,直接在外层)

      # NixOS 系统配置(nixos-rebuild switch --flake .#my-machine)
      nixosConfigurations.my-machine = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          ./nixos/configuration.nix
          home-manager.nixosModules.home-manager
          {
            home-manager.users.alice = import ./home.nix;
          }
        ];
      };

      # Home Manager 配置(home-manager switch --flake .#alice)
      homeConfigurations.alice = home-manager.lib.homeManagerConfiguration {
        pkgs = nixpkgs.legacyPackages."x86_64-linux";
        modules = [ ./home.nix ];
      };
    };
}

flake.lock — 依赖锁定

# 初始化 flake(创建 flake.lock)
nix flake lock

# 更新所有依赖到最新(更新 flake.lock)
nix flake update

# 只更新指定依赖
nix flake update nixpkgs

# 查看 flake 信息(包括 lock 的 commit)
nix flake info

# 检查 flake(验证 outputs 是否正确)
nix flake check

查看 flake.lock 的内容(JSON 格式):

{
  "nodes": {
    "nixpkgs": {
      "locked": {
        "lastModified": 1707832236,
        "narHash": "sha256-abc123...",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "de60d08...",  // 锁定到精确 commit
        "type": "github"
      }
    }
  },
  "root": {
    "inputs": {
      "nixpkgs": "nixpkgs"
    }
  },
  "version": 7
}

常用 Flake 命令

# 构建包
nix build .                # 构建默认包(packages.default)
nix build .#my-app         # 构建指定包
ls result/                 # 查看构建结果

# 运行应用
nix run .                  # 运行默认应用
nix run .#my-app -- --help # 传递参数

# 开发环境
nix develop                # 进入默认 devShell
nix develop .#ci           # 进入指定 devShell

# NixOS 重建(使用 flake 配置)
sudo nixos-rebuild switch --flake .#my-machine
sudo nixos-rebuild switch --flake github:myuser/nixos-config#my-machine

# 临时使用其他 flake 中的包
nix shell github:some-user/some-flake#some-package

将 shell.nix 迁移到 Flakes

迁移前(shell.nix)

# shell.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    nodejs_20
    yarn
  ];
}

迁移后(flake.nix)

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

  outputs = { self, nixpkgs }:
    let
      pkgs = nixpkgs.legacyPackages."x86_64-linux";
    in {
      devShells."x86_64-linux".default =
        pkgs.mkShell {
          buildInputs = with pkgs; [
            nodejs_20
            yarn
          ];
        };
    };
}
inputs.follows 避免依赖重复

当多个 inputs 都依赖 nixpkgs 时,使用 follows 确保它们使用同一个版本,避免 store 中出现多份 nixpkgs:

inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  home-manager.inputs.nixpkgs.follows = "nixpkgs";
  fenix.inputs.nixpkgs.follows = "nixpkgs";
};
本章小结

Flakes 是现代 Nix 的核心。flake.nix 声明依赖(inputs)和产物(outputs),flake.lock 将所有依赖锁定到精确 commit,保证跨机器、跨时间的完全一致性。掌握 packagesdevShellsnixosConfigurations 三种主要 output 类型,就能覆盖绝大多数使用场景。