为什么需要 Flakes?
在 Flakes 出现之前,Nix 依赖"频道"(channel)来获取 Nixpkgs。频道的问题是:
- 不同机器的频道更新时间不同,使用相同配置文件在不同时间/机器上构建结果不同
NIX_PATH环境变量决定<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,保证跨机器、跨时间的完全一致性。掌握 packages、devShells、nixosConfigurations 三种主要 output 类型,就能覆盖绝大多数使用场景。