1. Nix 包管理的核心理念
传统的包管理器(如 apt、yum、pacman)在全局文件系统上安装软件包,这种方式面临几个根本性问题:依赖冲突、难以回滚、构建不可复现。Nix 包管理器通过以下创新解决了这些问题:
- 隔离安装:每个包安装在唯一的
/nix/store/<hash>-<name>-<version>目录中,不同版本可以共存而互不影响 - 纯函数式构建:软件包的构建过程被描述为一个纯函数,给定相同的输入总是产生相同的输出
- 原子升级/回滚:因为包存储在独立的不可变路径中,升级和回滚只是切换符号链接,原子且安全
- 多用户支持:多个用户可以同时安装不同版本的同一软件包而互不干扰
2. nixpkgs 包仓库
2.1 仓库结构
nixpkgs 是世界上最大的软件包仓库之一,包含超过 10 万个软件包。它的核心是一个庞大的 Nix 表达式集合,所有软件包的构建方式都用 Nix 语言描述。
# ~/.nix-defexpr/channels/nixpkgs 是默认的 nixpkgs 路径
# 其核心是 pkgs/top-level/all-packages.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.hello # GNU Hello, 世界!
pkgs.git # Git 版本控制
pkgs.vim # Vim 编辑器
pkgs.nodejs_20 # Node.js 20
pkgs.rustc # Rust 编译器
pkgs.go # Go 编译器
pkgs.python311 # Python 3.11
2.2 频道(Channels)
频道是 nixpkgs 的发布渠道。更新频道相当于更新软件包索引:
# 列出已订阅的频道
nix-channel --list
# 添加官方频道
nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs
# 更新频道(下载最新包定义)
nix-channel --update
# 移除频道
nix-channel --remove nixpkgs
常用频道:
| 频道 | 用途 | 更新频率 |
|---|---|---|
| nixpkgs-unstable | 最新软件包 | 每日 |
| nixos-23.11 | NixOS 23.11 稳定版 | 安全更新 |
| nixos-24.05 | NixOS 24.05 稳定版 | 安全更新 |
| nixpkgs-darwin | macOS 专用 | 与 unstable 同步 |
2.3 搜索软件包
# 搜索包(支持模糊匹配)
nix search nixpkgs nodejs
nix search nixpkgs "firefox"
# 查看包信息
nix eval nixpkgs#nodejs.version
nix eval nixpkgs#nodejs.meta.description
nix eval nixpkgs#nodejs.meta.license
3. Derivation:包的构建公式
3.1 Derivation 的基本概念
在 Nix 中,derivation 是软件包的构建公式。它描述了如何从源码(或其他构建产物)编译出一个软件包。
{ stdenv, fetchurl }:
stdenv.mkDerivation {
pname = "hello";
version = "2.12.1";
src = fetchurl {
url = "mirror://gnu/hello/hello-2.12.1.tar.gz";
sha256 = "sha256-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=";
};
# 构建依赖(只存在于构建过程,不出现在运行时)
nativeBuildInputs = [ ];
# 运行时依赖
buildInputs = [ ];
# configure 选项
configureFlags = [ "--disable-nls" ];
meta = with stdenv.lib; {
description = "GNU Hello, 世界!程序";
license = licenses.gpl3Plus;
platforms = platforms.all;
};
}
3.2 构建哈希
每个包的路径包含其 store path hash,这是对其构建过程所有输入(源码、依赖、构建脚本)的加密哈希。
/nix/store/1g1z6a0j0b7p3z7b3b2n1g0p3z7b3b2n-hello-2.12.1
└────────────────────────────────┘
store path hash (base-32 编码)
这个哈希确保了:
- 相同的源码和依赖 → 相同的路径 → 自动复用已有构建
- 任何输入的改变 → 新的路径 → 新的构建
3.3 查看包的 store path
# 查看 hello 包的 store path
nix-build '<nixpkgs>' -A hello --no-out-link
# 查看包的依赖关系
nix-store --query --tree $(nix-build '<nixpkgs>' -A hello --no-out-link)
# 查看哪些包依赖于某个包
nix-store --query --referrers /nix/store/...-hello-2.12.1
4. 常用包管理命令
4.1 nix-env 命令行工具
# 安装包(到当前用户环境)
nix-env -iA nixpkgs.hello
nix-env -iA nixpkgs.git
# 列出已安装的包
nix-env -q
nix-env -q --out-path # 显示 store path
# 卸载包
nix-env -e hello
# 升级所有包
nix-env -u
nix-env -u hello # 升级指定包
# 回滚到上一个环境
nix-env --rollback
# 列出历史环境(用于回滚)
nix-env --list-generations
# 切换到指定版本
nix-env --switch-generation 42
4.2 临时使用包
# nix-shell:进入包含指定包的临时 shell
nix-shell -p python39 nodejs git
# 在临时环境中运行命令(不进入交互式 shell)
nix-shell -p jq --run "jq --version"
# 使用特定版本的包
nix-shell -p python39 python310 python311
4.3 构建包
# 从 nixpkgs 构建包
nix-build '<nixpkgs>' -A hello
# 构建当前目录的 default.nix
nix-build
# 构建并创建结果链接
nix-build -o my-hello '<nixpkgs>' -A hello
# 使用 Flakes 构建
nix build nixpkgs#hello
4.4 清理垃圾
# 删除未被任何环境引用的包
nix-collect-garbage
# 删除超过 30 天的旧版本
nix-collect-garbage --delete-older-than 30d
# 查看 store 大小
nix store info
# 优化 store(硬链接重复文件)
nix store optimise
5. 自定义包
5.1 覆盖现有包
使用 config.packageOverrides 修改现有包的属性:
# ~/.config/nixpkgs/config.nix
{
packageOverrides = pkgs: {
# 自定义包的 configure flags
nginx = pkgs.nginx.override {
modules = with pkgs.nginxModules; [ rtmp dav ];
};
# 使用特定版本的源码
my-vim = pkgs.vim.overrideAttrs (oldAttrs: {
version = "9.1.0-custom";
src = pkgs.fetchFromGitHub {
owner = "vim";
repo = "vim";
rev = "v9.1.0";
sha256 = "sha256-xxxxxxxxxx=";
};
});
};
}
5.2 创建私有包
# my-package.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.stdenv.mkDerivation {
pname = "my-app";
version = "0.1.0";
src = pkgs.fetchgit {
url = "https://github.com/example/my-app.git";
rev = "abc123";
sha256 = "sha256-xxxxxxxxxx=";
};
buildInputs = with pkgs; [ nodejs yarn ];
buildPhase = ''
yarn install --frozen-lockfile
yarn build
'';
installPhase = ''
mkdir -p $out/bin
cp -r dist $out/
makeWrapper ${pkgs.nodejs}/bin/node $out/bin/my-app \
--add-flags "$out/dist/index.js"
'';
meta = with pkgs.lib; {
description = "我的应用";
license = licenses.mit;
};
}
6. 多用户与权限
Nix 支持多用户安装模式,每个用户有自己的 profile:
/root/.nix-profile # root 用户的包
/home/alice/.nix-profile # alice 用户的包
/home/bob/.nix-profile # bob 用户的包
系统级的包安装在 /nix/var/nix/profiles/default,对系统上的所有用户可用。
# 系统级安装(需要 root)
nix-env -iA nixpkgs.nginx --profile /nix/var/nix/profiles/default
# 用户级安装(默认)
nix-env -iA nixpkgs.vim
7. 总结
Nix 包管理的核心优势在于:
| 特性 | 说明 | 对比传统包管理器 |
|---|---|---|
| 隔离安装 | 每个版本独立目录 | 传统:全局覆盖 |
| 可复现构建 | 相同输入 → 相同输出 | 传统:构建环境不固定 |
| 原子升级 | 符号链接切换,失败可回滚 | 传统:中途失败难以恢复 |
| 多版本共存 | 不同版本互不干扰 | 传统:只能安装一个版本 |
| 依赖追踪 | 精确的依赖树,无幽灵依赖 | 传统:容易产生依赖缺失 |
掌握 Nix 包管理是进入 Nix 生态的第一步。接下来的文章将深入讲解如何使用 Nix 构建可复现的开发环境、配置 NixOS 系统以及利用 Flakes 管理项目。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。