Example Versions and Verification Scope#
This tutorial retains examples using the NixOS/Home Manager 24.05 branches to illustrate structure, not to recommend deploying this historical release. Real configurations should use compatible, supported branches, retain the existing stateVersion, and check interfaces against the locked versions. Placeholder commits/hashes and ellipses must be replaced. The code has not been built or run on the target system.
This chapter covers Nix's most important advance. Flakes provide reproducible, version-locked project definitions.
What Are Flakes?#
Flakes are an experimental Nix feature that provides:
- Reproducibility: all dependencies are locked through flake.lock
- Composability: easily combine multiple projects
- Standardization: a consistent input/output interface
Enabling Flakes#
NixOS Systems
# /etc/nixos/configuration.nix
{ pkgs, ... }: {
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# 允许非自由软件
nixpkgs.config.allowUnfree = true;
}Non-NixOS Systems
# ~/.config/nix/nix.conf
experimental-features = nix-command flakesOr enable them temporarily with command-line flags:
nix --extra-experimental-features "nix-command flakes" <命令>Flake File Structure#
Basic Syntax
{
# === 输入依赖 ===
inputs = {
# 标准格式: type:location?ref
# type = github, gitlab, sourcehut, git, path, etc.
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
# 本地路径
my-package.url = "path:./my-package";
# Git 仓库
my-project.url = "git+https://github.com/user/repo?ref=main";
};
# === 输出定义 ===
outputs = { self, nixpkgs, ... }@inputs: {
# packages, devShells, nixosConfigurations, etc.
};
}Complete Example
{
description = "我的项目";
# ========== 输入依赖 ==========
inputs = {
# nixpkgs 官方包集合
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
# 跟随 nixpkgs(使用同一版本)
home-manager = {
url = "github:nix-community/home-manager/release-24.05";
inputs.nixpkgs.follows = "nixpkgs";
};
# 锁定到特定 commit
nixpkgs-old.url = "github:NixOS/nixpkgs/a1b2c3d4";
# 本地 Flake
my-local.url = "path:/home/user/my-flake";
};
# ========== 输出定义 ==========
outputs = { self, nixpkgs, home-manager, ... }@inputs:
let
# 定义系统平台
supportedSystems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
# 为每个系统生成配置(自动化工具)
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
# 获取对应平台的 nixpkgs
pkgsFor = system: nixpkgs.legacyPackages.${system};
in
{
# === 软件包 ===
packages = forAllSystems (system: {
default = (pkgsFor system).callPackage ./default.nix {};
my-app = (pkgsFor system).callPackage ./my-app.nix {};
});
# === 开发环境 ===
devShells = forAllSystems (system: {
default = (pkgsFor system).mkShell {
packages = with (pkgsFor system); [
git nodejs jq
];
};
});
# === NixOS 配置 ===
nixosConfigurations.my-server = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
];
specialArgs = { inherit inputs; };
};
# === Home Manager 配置 ===
homeConfigurations.alice = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
modules = [ ./home.nix ];
extraSpecialArgs = { inherit inputs; };
};
# === 格式化检查 ===
formatter = forAllSystems (system: (pkgsFor system).alejandra);
};
}Inputs in Detail#
Common Input Sources
{
inputs = {
# GitHub(最常用)
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
# 指定分支/标签/commit
nixpkgs-unstable.url = "github:NixOS/nixpkgs/master";
nixpkgs-some-commit.url = "github:NixOS/nixpkgs/abc123def";
# GitLab
gitlab-repo.url = "gitlab:owner/repo/main";
# SourceHut
srht-repo.url = "sourcehut:owner/repo";
# 任意 Git 仓库
my-git.url = "git+https://example.com/repo.git?ref=main";
my-git-ssh.url = "git+ssh://git@github.com/owner/repo";
# 本地路径
local-pkg.url = "path:./local-package";
# 跟随其他输入(避免重复下载)
home-manager = {
url = "github:nix-community/home-manager/release-24.05";
# 使用与 nixpkgs 相同的版本
inputs.nixpkgs.follows = "nixpkgs";
};
# 禁用某些输入
some-input.inputs.unused.follows = "";
};
}Input Attributes
{
inputs = {
my-input = {
url = "github:owner/repo";
# 将内容当作普通源码而非 Flake;固定 commit 应写入 URL/rev
flake = false; # 非 flake 输入(纯内容)
# 子目录
# 使用 Flake 子目录(URL 查询参数)
# url = "github:owner/repo?dir=subdirectory";
};
};
}Flake Commands in Detail#
Basic Commands
# 查看 flake 信息
nix flake info
nix flake metadata
# 仅检查求值,不构建 checks
nix flake check --no-build
# 求值并构建 checks(不激活系统)
nix flake check
# 更新所有依赖
nix flake update
# 更新特定输入
nix flake update nixpkgs
nix flake update home-manager
# 编辑 flake
nix flake edit
# 克隆 flake 仓库
nix flake clone <flake-url>
# 显示 flake 目录
nix flake init # 在当前目录初始化Build Commands
# 构建默认包
nix build
# 构建特定包
nix build .#my-package
nix build .#packages.x86_64-linux.my-app
# 构建并输出路径
nix build .#my-package --out-link ./result
# 构建并打印路径
nix build .#my-package --print-out-paths
# 从其他 flake 构建
nix build github:nixos/nixpkgs#hello
nix build gitlab:owner/repo#packageRun Commands
# 运行默认包
nix run
# 运行特定包
nix run .#my-app
# 运行临时软件包(不安装)
nix run nixpkgs#htop
nix run nixpkgs#nodejs -- --version
nix run nixpkgs#cowsay -- "Hello Nix"
# 从 URL 运行
nix run github:owner/repo#scriptDevelopment Environments
# 进入开发环境
nix develop
nix develop .#default
# 进入特定 shell
nix develop .#python-dev
# 临时进入环境执行命令
nix develop --command cargo build
nix develop .#rust --command rustc --version
# nix shell 是新 CLI 的临时包环境,与 nix develop 不同
nix shell .#my-package
nix shell nixpkgs#git nixpkgs#vimSystem Configuration
# NixOS
sudo nixos-rebuild switch --flake .#my-host
sudo nixos-rebuild build --flake .#my-host
sudo nixos-rebuild test --flake .#my-host
# 查看系统配置
nixos-rebuild build-vm --flake .#my-host
# Home Manager
home-manager switch --flake .#username
home-manager build --flake .#username
# Darwin (nix-darwin)
darwin-rebuild switch --flake .#my-macNixOS Flake Configuration#
Single-Host Configuration
# flake.nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
outputs = { self, nixpkgs }: {
nixosConfigurations.my-laptop = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
{
# 自动传递 self 作为 specialArgs
_module.args.self = self;
}
];
};
};
}Multi-Host Configuration
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
inputs.home-manager.url = "github:nix-community/home-manager/release-24.05";
outputs = { self, nixpkgs, home-manager }:
let
mkSystem = { hostname, system, users }: nixpkgs.lib.nixosSystem {
inherit system;
modules = [
./hosts/${hostname}/hardware-configuration.nix
./hosts/${hostname}/configuration.nix
./modules/common
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users = nixpkgs.lib.genAttrs users (user:
import ./home/${user}.nix
);
}
];
specialArgs = { inherit self; };
};
in
{
nixosConfigurations = {
# 笔记本
laptop = mkSystem {
hostname = "laptop";
system = "x86_64-linux";
users = [ "alice" ];
};
# 台式机
desktop = mkSystem {
hostname = "desktop";
system = "x86_64-linux";
users = [ "alice" "bob" ];
};
# 服务器
server = mkSystem {
hostname = "server";
system = "x86_64-linux";
users = [ "deploy" ];
};
};
};
}Modular Flakes#
Directory Structure
~/nixos-config/
├── flake.nix # 入口
├── flake.lock # 锁定文件
├── lib/ # 自定义库函数
│ └── default.nix
├── modules/ # 可复用模块
│ ├── core/ # 核心系统配置
│ ├── desktop/ # 桌面环境
│ ├── development/ # 开发环境
│ └── services/ # 服务
├── hosts/ # 主机特定配置
│ ├── laptop/
│ │ ├── default.nix
│ │ └── hardware-configuration.nix
│ └── server/
│ ├── default.nix
│ └── hardware-configuration.nix
├── home/ # Home Manager 配置
│ ├── alice.nix
│ └── bob.nix
└── pkgs/ # 自定义包
└── default.nixModular Entry Point
# flake.nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
outputs = inputs@{ self, nixpkgs }:
let
inherit (nixpkgs) lib;
# 系统类型
systems = [ "x86_64-linux" "aarch64-linux" ];
# 为每个系统应用函数
forEachSystem = f: lib.genAttrs systems (sys: f nixpkgs.legacyPackages.${sys});
in
{
# 导入 lib
lib = import ./lib { inherit lib; };
# 导入包
packages = forEachSystem (pkgs: import ./pkgs { inherit pkgs; });
# NixOS 配置
nixosConfigurations = {
laptop = lib.nixosSystem {
system = "x86_64-linux";
modules = [
# 核心模块
./modules/core
./modules/desktop
./modules/development
# 主机特定
./hosts/laptop
# 本地包
{ nixpkgs.overlays = [ self.overlays.default ]; }
];
specialArgs = { inherit self inputs; };
};
};
# Overlay
overlays.default = final: prev: {
my-packages = self.packages.${final.system};
};
};
}Flake.lock in Detail#
Lockfile Structure
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1698611440,
"narHash": "sha256-...",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a1b2c3d4...",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}Locking Commands
# 创建/更新锁定
nix flake lock
# 更新特定输入
nix flake update nixpkgs
# 使用特定版本
# 临时覆盖不写入锁文件;持久固定请先修改 flake.nix
nix flake metadata --override-input nixpkgs github:NixOS/nixpkgs/<rev>
# 从锁定文件恢复
nix flake metadata --reference-lock-file ./old-flake.lock
# 查看锁定信息
nix flake metadata --jsonPractical Examples#
Project Templates
# flake.nix - Python 项目
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
python = pkgs.python311;
my-python-packages = ps: with ps; [
requests
flask
pytest
black
mypy
];
in
{
devShells.${system}.default = pkgs.mkShell {
name = "python-project";
packages = with pkgs; [
(python.withPackages my-python-packages)
git
just # 命令运行器
];
shellHook = ''
# python.withPackages 的包装器提供声明的包。
# 不设置虚假的 VIRTUAL_ENV;如需 venv,应单独创建并激活。
echo "Python 开发环境已加载"
python --version
'';
};
packages.${system}.default = pkgs.python311Packages.buildPythonApplication {
pname = "my-app";
version = "1.0.0";
src = ./.;
propagatedBuildInputs = with pkgs.python311Packages; [
requests flask
];
};
};
}Complete System Configuration
# flake.nix
{
description = "NixOS 完全配置";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
home-manager = {
url = "github:nix-community/home-manager/release-24.05";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-doom-emacs = {
url = "github:nix-community/nix-doom-emacs";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = inputs@{ self, nixpkgs, home-manager, ... }:
let
lib = nixpkgs.lib;
mkSystem = { config, system ? "x86_64-linux" }: lib.nixosSystem {
inherit system;
modules = [
config
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
# 自动传递 inputs
_module.args.inputs = inputs;
}
];
};
in
{
nixosConfigurations = {
laptop = mkSystem {
config = ./hosts/laptop;
};
work = mkSystem {
config = ./hosts/work;
};
};
# 格式化工具
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.alejandra;
# 模板
templates.default = {
path = ./templates/basic;
description = "基础项目模板";
};
};
}Frequently Asked Questions#
How Do I Inspect the Outputs a Flake Provides?
nix flake show
nix flake show github:owner/repoHow Do I Debug a Build Failure?
# 进入项目开发环境(不等于自动复现失败 derivation 的完整环境)
nix develop .#devShells.x86_64-linux.default -L
# 查看详细日志
nix build .#package --print-build-logs
nix build .#package -LHow Do I Reference a Local Path?
{
inputs.local-pkg.url = "path:/absolute/path/to/package";
# 或相对路径
inputs.my-config.url = "path:./my-config";
}How Do I Support Legacy Entry Points?
nix-build does not automatically convert a flake into a traditional expression. The project needs to provide default.nix/shell.nix; flake-compat can adapt the locked flake. Only after adding that adapter should you use:
nix-buildBest Practices#
- Always lock versions: use
nix flake lock - Separate system configurations: a dedicated directory for each host
- Use follows: avoid duplicate downloads
- Update regularly: but not blindly
- Back up the lock file: keep it in version control
References & further reading
Adapted from technical notes. Examples use generic names.
Continue along a reading path
Flakes: project and dependency management
- 01Flake structure: inputs and outputs
- 02flake.lock and reproducibility
- 03Flake Utils and project templates
- 04External Flake references and collaboration
- 05Migrating NixOS configuration to Flakes
- 06A tutorial on Flake projects and dependency management
- 07How Flake updates work
- 08Rolling back and manually editing flake.lock