NixOS 26.05 "Yarara" 深度实战:当声明式配置成为开发环境的终极形态——从 Nix Flakes 到 devshell、从原子化回滚到跨平台复现的完全指南(2026)
前言:为什么你需要的不是又一个包管理器
作为一名工作超过五年的程序员,我换过无数开发环境:Ubuntu → Arch → macOS,每个系统重装都意味着几个晚上的折腾;Python 环境用 virtualenv、conda、pyenv 轮着来;Node.js 版本用 nvm 管理,Rust 用 rustup,Go 用 goenv——结果就是 ~/.bashrc 越来越长,每台机器的配置都不一样,「在我机器上能跑」成了日常谎言。
直到我遇见了 Nix。
Nix 不是另一个包管理器。它是一套声明式、可复现、原子化的系统管理与软件部署哲学。NixOS 则是这套哲学在操作系统层面的完整实现。2026年6月发布的 NixOS 26.05 "Yarara",默认启用 Linux 6.18 LTS 内核、集成 systemd Stage 1 启动环境、更新了 20,000+ 个软件包,更是将这套体系推向了新的成熟度。
本文将以一个务实程序员的视角,深入讲解 Nix 的核心机制、Nix Flakes 的现代化用法、NixOS 26.05 的关键更新,以及如何用 Nix 构建真正可复现的开发环境。文章较长,但每一节都有实战代码,保证你读完后能直接动手。
一、Nix 的核心哲学:函数式包管理
1.1 传统包管理器的根本缺陷
在讨论 Nix 之前,我们先解剖一下传统包管理器的问题:
Debian apt 示例:当你执行 apt install python3.11 时,系统将 /usr/bin/python3 指向 Python 3.11 的二进制文件。如果你同时需要 Python 3.9 和 Python 3.11 怎么办?apt 做不到。你必须借助虚拟环境或容器。
macOS Homebrew 示例:brew install python@3.11 将 Python 安装到 /usr/local/opt/python@3.11,但如果项目依赖特定版本的 numpy,而该 numpy 编译时链接了特定版本的 OpenSSL,就会产生「依赖地狱」。
根本问题:传统包管理器是破坏性更新——安装新版本会替换旧版本,没有安全的回退机制;包与包之间的依赖关系无法精确控制。
1.2 Nix 的解法:纯粹函数式包管理
Nix 的核心理念来自函数式编程:相同的输入永远产生相同的输出,且无副作用。
# 传统包管理:安装意味着覆盖全局状态
# apt install nginx → /usr/bin/nginx 被替换
# Nix 包管理:安装产生新的「世代」,旧状态完整保留
# nix-env -i nginx → /nix/store/xxx-nginx → 生成新世代
当你用 Nix 安装 Python 3.11 时,它不是替换系统 Python,而是将 Python 3.11 安装到 Nix Store 的唯一路径:
/nix/store/3rq7hq4n7a4s9f6g-python3.11-3.11.8/lib/python3.11/site-packages/
注意这个路径中的哈希值 3rq7hq4n7a4s9f6g,它是根据 Python 3.11.8 这个包及其所有依赖项(glibc、gcc、openssl 等)的完整构建图计算出来的。任何依赖链的微小变化都会导致路径哈希变化,这意味着:
- Python 3.11.8 + glibc-2.38 + openssl-3.0.13 → 路径 A
- Python 3.11.8 + glibc-2.38 + openssl-3.1.0 → 路径 B(完全不同的路径)
- Python 3.9.17 + glibc-2.38 + openssl-3.0.13 → 路径 C
这就是 Nix 的可复现性基础:每个包都有唯一的、基于其完整依赖图的路径。
1.3 Nix Store 与垃圾回收
因为每个包都有固定路径,Nix 可以安全地管理多个版本的同一软件而不会冲突。那么旧版本如何清理?
# 查看当前_profile 链接指向的世代
ls -la ~/.nix-profile
# → ~/.nix-profile -> /nix/var/nix/profiles/default
# 查看所有世代
nix-env --list-generations
# 95 2026-06-10 14:23:12
# 96 2026-06-12 09:45:33
# 97 2026-06-15 08:11:20 ← 当前活跃
# 切换到旧世代(原子操作)
nix-env --switch-generation 96
# 垃圾回收:删除不再被任何_profile引用的 store 对象
nix-collect-garbage -d
关键洞察:Nix 的 GC 是安全的,因为它基于引用计数——只有被活跃 profile 链接的 store 对象才会被保留。这意味着你可以放心尝试任何包,不用担心「卸载不干净」。
二、NixOS 26.05 "Yarara" 核心更新解析
2.1 systemd Stage 1:启动流程的重大变革
NixOS 26.05 最大的系统级变化是默认启用 systemd Stage 1。这意味着什么?
传统 NixOS 启动流程(脚本式 Stage 1):
BIOS/UEFI → GRUB → Linux 内核 → NixOS Stage 1(bash脚本)→ Stage 2(systemd)
NixOS 26.05 启动流程(systemd Stage 1):
BIOS/UEFI → GRUB → Linux 内核 → systemd(统一的初始化系统)
这对普通用户影响不大,但有几个重要意义:
- 与主流 Linux 生态更加一致,降低学习和维护成本
- 方便与 systemd 生态的工具(如 systemd-boot, systemd-homed)集成
- 简化了 NixOS 自身的基础设施代码
如果你仍在使用旧版 Stage 1,NixOS 团队已明确警告:在 NixOS 26.11 中将彻底移除旧版方案,届时必须迁移。
# 配置文件(如果你需要强制使用新版 Stage 1)
{ config, pkgs, ... }: {
# systemd Stage 1 已为 NixOS 26.05 默认启用,无需额外配置
# 但如果需要显式控制:
boot.initrd.systemd.enable = true; # 默认 true
# 旧版脚本式 Stage 1(已弃用,将在 26.11 移除)
# boot.initrd.useScripts = true; # ← 不要用这个
}
2.2 Linux 6.18 LTS 内核
NixOS 26.05 默认搭载 Linux 6.18 LTS 内核,同时保留多个内核分支供选择:
# 切换内核版本(示例:切换到最新的主线内核)
{ config, pkgs, ... }: {
boot.kernelPackages = pkgs.linuxPackages_latest;
# 可选:linuxPackages_latest, linuxPackages_6_12, linuxPackages_xanmod 等
}
2.3 Nixpkgs 大规模更新
26.05 版本带来了 20,442 个新增包、20,641 个更新包、17,532 个移除包的三万次级变更,Nixpkgs 总包数量已超过 100,000 个。其中值得特别关注:
| 包类型 | 代表更新 |
|---|---|
| 编程语言 | Python 3.13, Node.js 24, Rust 2.0, Go 1.26 |
| 桌面环境 | GNOME 50, KDE Plasma 6.6.5, Sway 1.10 |
| 工具链 | GCC 15, LLVM 21, Go 1.26, .NET 10 |
| 内核 | Linux 6.18 LTS, 6.12 LTS |
2.4 x86_64-darwin 退出历史舞台
NixOS 26.05 是最后一个正式支持 Intel Mac 的 Nixpkgs 版本。从 Nixpkgs 26.11 开始,项目将全面转向 Apple Silicon 平台。这意味着:
- 仍在使用 Intel Mac 的 NixOS/Nix 用户需要尽快迁移
- Apple Silicon 平台的 Nix 体验将进一步优化
- Nix-on-Droid(Android 上的 Nix 环境)持续活跃,移动端开发场景拓展
三、Nix Flakes:现代 Nix 的正确打开方式
3.1 什么是 Flakes?
Nix Flakes 是 2020 年引入的实验性特性,经过多年打磨已在 2026 年成为事实上的标准用法。它解决了传统 Nix 的几个核心问题:
- 依赖版本锁定:Flakes 使用
flake.lock文件精确锁定所有依赖的版本 - 规范化接口:每个 Flake 提供确定的
outputs接口,不再需要理解复杂的 Nix 表达式语法 - 跨仓库引用:可以直接引用其他 Flake 的输出,就像 npm 的
package.json一样简洁
为什么不用传统 shell.nix:传统 shell.nix 依赖 NIX_PATH 环境变量来解析 nixpkgs,这在不同机器上可能指向不同版本。Flakes 的 flake.lock 彻底解决了这个问题。
3.2 Flake 的基本结构
# flake.nix — 项目根目录
{
description = "我的全栈项目开发环境";
# 输入:依赖声明
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
# 引入 flake-utils 简化跨平台输出
flake-utils.url = "github:numtide/flake-utils";
# 引入 home-manager 管理用户环境
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
# 引入专门为 Nix 构建的 Rust 工具链
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
# 输出:定义 flake 提供的所有资源
outputs = { self, nixpkgs, flake-utils, home-manager, rust-overlay, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
# 叠加 overlay,扩展 nixpkgs
overlays = [
rust-overlay.overlays.default
(final: prev: {
# 自定义包:添加项目中常用的工具
mytools = prev.writeShellScriptBin "mytools" ''
echo "My custom tools environment"
${prev.openssl}/bin/openssl version
'';
})
];
# 基础 pkgs(带 overlay)
pkgs = import nixpkgs {
inherit system overlays;
config.allowUnfree = true;
};
# 为特定开发场景定制的 pkgs
pkgs-python = pkgs.python3.override {
packageOverrides = p: {
numpy = p.numpy_1_26;
pandas = p.pandas;
};
};
in
{
# 1. devShell:进入开发环境
devShells.default = pkgs.mkShell {
# 构建时依赖(不会被带入 shell)
buildInputs = [
# 基础工具
pkgs.git
pkgs.curl
pkgs.wget
# 语言运行时
pkgs.nodejs_22
pkgs.python313
(pkgs.python313.withPackages (ps: with ps; [
ps.requests
ps.fastapi
ps.sqlalchemy
ps.black
ps.ruff
ps.ipython
ps.jupyterlab
]))
# 数据库
pkgs.postgresql_16
pkgs.redis_7_2
# 开发工具
pkgs.docker-client
pkgs.kubernetes-helm
pkgs.kubectl
pkgsterraform
pkgs.pkg-config
pkgs.libpqxx # PostgreSQL C++ driver
# 自定义工具
pkgs.mytools
];
# shell 钩子:进入时自动执行
shellHook = ''
echo "===== Nix 开发环境已激活 ====="
echo "Node.js: $(node --version)"
echo "Python: $(python3 --version)"
echo "PostgreSQL: ${pkgs.postgresql_16}/bin/pg_config"
echo ""
# 彩色提示符
export PS1="\[\033[1;32m\](nix) \[\033[0m\]\h:\w\$ "
'';
};
# 2. formatter:代码格式化
formatter = pkgs.nixfmt;
# 3. packages:可安装的包
packages = {
inherit (pkgs) mytools;
myapp = self.packages.${system}.myapp;
};
# 4. checks:本地测试
checks = {
# 运行本项目的测试套件
unit-tests = pkgs.runCommand "unit-tests" {
buildInputs = [ pkgs.nodejs_22 ];
} ''
cd ${self}
npm install
npm test
touch $out
'';
};
}
);
}
3.3 flake.lock:可复现的依赖锁定
当你首次运行 nix flake check 或 nix develop 时,Nix 会自动生成 flake.lock:
{
"locks": {
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1750300800,
"narHash": "sha256-aBcDeFgHi1234567890/XyZ+mnOpQrStUvWxYz345678=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "nixos-26.05-release",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
},
"home-manager": {
"locked": {
"lastModified": 1750200000,
"narHash": "sha256-xyz789AbCdEfGhIjKlMnOpQrStUvWxYz012345=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "a1b2c3d4e5f6",
"type": "github"
}
}
},
"version": 7
}
}
关键价值:flake.lock 将所有依赖的 Git 提交哈希固定下来。无论在哪台机器、什么时间运行 nix develop,只要 flake.lock 不变,得到的开发环境就完全一致。
# 更新依赖(相当于 npm update)
nix flake update
# 查看将要更新的内容
nix flake metadata
# 在特定 flake 输入上运行命令
nix run github:nix-community/home-manager -- --version
四、Nix devshell:可复现开发环境的实战
4.1 痛点:我们是如何把开发环境搞砸的
让我们用一个真实场景来理解 devshell 的价值:
项目需求:
- Python 3.11(项目依赖某些只在 3.11 测试过的库)
- Node.js 18(前端历史包袱)
- PostgreSQL 14(生产环境版本)
- OpenSSL 1.1.1(遗留 C++ 服务的依赖)
- Terraform 1.5.x(基础设施代码锁定)
传统解法:
# 每个工具都要单独管理
pyenv install 3.11.7
pyenv local 3.11.7
nvm install 18
nvm use 18
brew install postgresql@14
brew install openssl@1.1
brew install terraform@1.5
结果:机器 A 上跑通了;机器 B 上 pyenv 的 Python 路径不对;CI 上 nvm 没装;同事的 Apple Silicon Mac 上 Homebrew 的 Intel 版和 ARM 版路径不同……
4.2 Nix devshell 解法:一条命令进入完美环境
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
};
outputs = { self, nixpkgs }:
let
# 为每种技术栈创建专门的包集合
overlay = final: prev: {
# Python 3.11 with specific package overrides
python311-custom = prev.python311.withPackages (ps: with ps; [
ps.flask ps.django ps.fastapi ps.requests ps.psycopg2-binary
ps.redis ps.pydantic ps.sqlalchemy ps.alembic ps.black ps.ruff
ps.pytest ps.pytest-cov ps.ipython
]);
# Node.js 18 with global packages
nodejs18-custom = prev.nodejs18.overrideAttrs (oldAttrs: {
# 使用 package.json 安装全局 npm 包
postInstall = ''
npm install -g typescript ts-node nodemon
'';
});
# PostgreSQL 14 with specific configuration
postgresql14-custom = prev.postgresql_14.withPackages (ps: with ps; [
ps.pgAdmin4 ps.pgcli ps.postgis
]);
};
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ overlay ];
};
in
{
devShells.x86_64-linux.default = pkgs.mkShell {
name = "my-project-dev";
# 核心语言运行时
packages = with pkgs; [
# Python 3.11 with packages
python311-custom
# Node.js 18
nodejs18-custom
# 数据库
postgresql14-custom
redis
# 基础设施工具
terraform_1_5
awscli2
kubectl
kubernetes-helm
# C++ 工具链(编译遗留服务用)
gcc13
cmake
ninja
vcpkg
# 杂项
git
direnv
ripgrep
fd
fzf
tmux
gh
jq
yq-go
];
# 环境变量(精确到版本)
env = with pkgs; [
# Python 精确路径(供 IDE 使用)
"PYTHONPATH=${python311-custom}/lib/python3.11/site-packages"
# Node.js 版本标记
"NODE_VERSION=18"
# 数据库连接
"PGDATA=/tmp/pgdata"
"REDIS_URL=redis://localhost:6379"
# OpenSSL 路径(遗留 C++ 服务需要)
"OPENSSL_ROOT_DIR=${openssl_1_1_1}"
"OPENSSL_LIB_DIR=${openssl_1_1_1}/lib"
"OPENSSL_INCLUDE_DIR=${openssl_1_1_1}/include"
];
shellHook = ''
# 进入 shell 时的初始化
export PS1="\[\033[1;34m\](dev) \[\033[0m\]\u@\h:\w\$ "
# 启动本地 PostgreSQL
if ! pg_isready -q 2>/dev/null; then
echo "[dev] 启动本地 PostgreSQL..."
${postgresql14-custom}/bin/pg_ctl -D /tmp/pgdata -l /tmp/pg.log start
sleep 2
fi
# 启动 Redis
if ! ${redis}/bin/redis-cli ping > /dev/null 2>&1; then
echo "[dev] 启动 Redis..."
${redis}/bin/redis-server --daemonize yes --port 6379
fi
# 检查项目依赖
if [ -f requirements.txt ]; then
echo "[dev] Python 包已就绪 ($(python3 --version))"
fi
if [ -f package.json ]; then
echo "[dev] Node.js 环境已就绪 ($(node --version))"
fi
echo "[dev] 开发环境已就绪!"
'';
};
};
}
进入开发环境只需:
# 单条命令,完整复现
nix develop
# 输出:
# [dev] 启动本地 PostgreSQL...
# [dev] 启动 Redis...
# [dev] Python 环境已就绪 (Python 3.11.8)
# [dev] Node.js 环境已就绪 (v18.19.1)
# [dev] 开发环境已就绪!
这意味着什么? 新同事入职,只需:
git clone https://github.com/your-org/your-project
cd your-project
nix develop
# 瞬间拥有完全一致的开发环境,无需手动安装任何工具
4.3 direnv 集成:自动切换环境
配合 direnv,进入项目目录自动激活环境,离开时自动清理:
# 安装 direnv 的 nix 集成
nix-env -iA nixpkgs.direnv
# 项目根目录添加 .envrc
echo "use flake" > .envrc
direnv allow
# 现在:
# cd project → 自动激活 devShell
# cd .. → 自动退出
4.4 跨平台开发:Linux + macOS + Windows (WSL2)
Nix 的 flake 设计天然支持跨平台:
outputs = { self, nixpkgs, ... }:
# 为所有常用平台构建相同的开发环境
flake-utils.lib.eachDefaultSystem (system:
let pkgs = import nixpkgs { inherit system; };
in {
devShells.${system}.default = pkgs.mkShell {
packages = with pkgs; [ git nodejs python311 ];
};
}
)
# 等价于:
# devShells.x86_64-linux.default
# devShells.aarch64-linux.default
# devShells.x86_64-darwin.default (26.05 最后一个支持版本)
# devShells.aarch64-darwin.default
五、NixOS 系统配置:声明式 OS 管理
5.1 NixOS 的核心:configuration.nix
NixOS 与普通 Linux 发行版的根本区别在于:整个操作系统配置由一个 Nix 表达式定义。
# /etc/nixos/configuration.nix
{ config, pkgs, ... }:
{
imports = [
# 引入硬件特定配置(nixos-generate-config 自动生成)
./hardware-configuration.nix
# 引入 home-manager 模块
<home-manager/nixos>
];
# === Boot ===
boot.loader.grub.enable = true;
boot.loader.grub.device = "/dev/sda";
boot.kernelPackages = pkgs.linuxPackages_6_18;
# === 网络 ===
networking.hostName = "workstation";
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 80 443 3000 5432 ];
# === 用户 ===
users.users.qnnet = {
isNormalUser = true;
description = "Developer";
extraGroups = [
"wheel" # sudo 权限
"docker" # Docker 组
"libvirtd" # 虚拟化
];
# 声明式配置 shell
shell = pkgs.zsh;
};
# === 环境 ===
environment.systemPackages = with pkgs; [
vim
git
curl
htop
bat # 带语法高亮的 cat 替代品
exa # ls 替代品
fd # find 替代品
ripgrep # grep 替代品
tmux
neovim
];
# === 编程语言 ===
# Python
environment.systemPackages = (with pkgs; [
python313
python313Packages.pip
python313Packages.virtualenv
]) ++ (with pkgs.python313Packages; [
black
ruff
mypy
pytest
]);
# === 数据库 ===
services.postgresql = {
enable = true;
package = pkgs.postgresql_16;
settings = {
max_connections = 200;
shared_buffers = "2GB";
effective_cache_size = "6GB";
};
initialScript = pkgs.writeText "init.sql" ''
CREATE DATABASE myapp;
CREATE USER myapp WITH PASSWORD 'dev_password';
GRANT ALL PRIVILEGES ON DATABASE myapp TO myapp;
'';
};
services.redis = {
enable = true;
package = pkgs.redis_7_2;
settings = {
maxmemory = "256mb";
maxmemory-policy = "allkeys-lru";
};
};
# === Docker ===
virtualisation.docker = {
enable = true;
autoPrune.enable = true;
daemon.settings = {
"live-restore" = true;
"log-driver" = "json-file";
"log-opts" = {
"max-size" = "10m";
"max-file" = "3";
};
};
};
# === 开发工具 ===
programs = {
zsh = {
enable = true;
enableCompletion = true;
histSize = 100000;
shellAliases = {
ll = "exa -la --git --icons";
g = "git";
gs = "git status";
k = "kubectl";
d = "docker";
dps = "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'";
dc = "docker compose";
py = "python3";
};
};
git = {
enable = true;
userName = "Your Name";
userEmail = "your@email.com";
extraConfig = {
init = { defaultBranch = "main"; };
pull = { rebase = false; };
push = { default = "current"; };
core = { editor = "vim"; };
alias = {
co = "checkout";
br = "branch";
st = "status";
lg = "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit";
};
};
};
vim = {
enable = true;
defaultEditor = true;
};
};
# === Home Manager ===
home-manager.users.qnnet = { pkgs, ... }: {
home.packages = with pkgs; [
# GUI 应用
firefox
vscode
slack
spotify
# 命令行增强
bat
exa
fzf
ripgrep
tmux
autojump # 智能 cd
the_silver_searcher # ag
# 字体
(nerdfonts.override { fonts = [ "JetBrainsMono" "FiraCode" ]; })
];
programs = {
vscode = {
enable = true;
extensions = with pkgs.vscode-extensions; [
ms-python.python
mskelton.npm-outdated
dbaeumer.vscode-eslint
esbenp.prettier-vscode
rust-lang.rust-analyzer
golang.go
ms-azuretools.vscode-docker
];
userSettings = {
"editor.formatOnSave" = true;
"editor.tabSize" = 2;
"files.insertFinalNewline" = true;
"files.trimTrailingWhitespace" = true;
"python.linting.enabled" = true;
"python.linting.ruffEnabled" = true;
"rust-analyzer.checkOnSave.command" = "clippy";
};
};
zsh = {
enable = true;
enableAutosuggestions = true;
enableCompletion = true;
plugins = [
{
name = "fzf-tab";
src = pkgs.fzf-tab;
}
];
};
tmux = {
enable = true;
clockFormat = 24;
keyMode = "vi";
baseIndex = 1;
shell = "${pkgs.zsh}/bin/zsh";
extraConfig = ''
# 鼠标支持
set -g mouse on
# 分割窗口
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# 重新加载配置
bind r source-file ~/.config/tmux/tmux.conf \; display "Config reloaded!"
# 复制模式 vi 键绑定
setw -g mode-keys vi
bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi y send -X copy-selection-and-cancel
# 状态栏
set -g status-style 'bg=#1e1e2e fg=#cdd6f4'
set -g status-left-length 40
set -g status-right-length 60
set -g status-left " #[fg=#89b4fa]#{session_name} "
set -g status-right "#[fg=#cdd6f4]%Y-%m-%d %H:%M "
'';
};
};
xdg.configFile."starship.toml".text = ''
[character]
success_symbol = "[➜](bold green)"
[git_branch]
symbol = " "
style = "bold purple"
[python]
python_symbol = " "
format = "[$symbol($version )(\($virtualenv) )]($style)"
[nodejs]
symbol = " "
'';
};
# === 时区与语言 ===
time.timeZone = "Asia/Shanghai";
i18n.defaultLocale = "zh_CN.UTF-8";
i18n.supportedLocales = [ "zh_CN.UTF-8" "en_US.UTF-8" ];
# === 系统优化 ===
powerManagement.enable = true;
services.thermald.enable = true;
# === 自动更新 ===
system.autoUpgrade.enable = true;
system.autoUpgrade.channel = "https://nixos.org/channels/nixos-26.05";
# === 最终配置 ===
system.stateVersion = "26.05";
}
5.2 原子化配置切换:永不丢失的工作系统
这是 NixOS 最令人安心的特性——配置切换是原子操作,不会搞崩系统:
# 1. 编辑配置
sudo vim /etc/nixos/configuration.nix
# 2. 验证配置语法(不实际构建)
sudo nixos-rebuild dry-activate
# 3. 构建并切换(新配置有问题?自动回退到旧配置!)
sudo nixos-rebuild switch --upgrade
# 4. 查看所有配置世代
nixos-rebuild list-generations
# 示例输出:
# Generation 156 2026-06-18 10:23:12 (current)
# Generation 155 2026-06-17 09:11:45
# Generation 154 2026-06-15 22:05:33
# Generation 153 2026-06-10 16:45:21
# 5. 回滚到上一代(紧急情况!)
sudo nixos-rebuild switch --rollback
# 6. 回滚到任意历史世代
sudo nixos-rebuild switch -I nixos-config=/nix/var/nix/profiles/system-154/kernel-modules.nix
对比传统 Linux:传统发行版更新失败后,你面对的是一个无法启动的系统,只能靠 Live USB 救援。NixOS 最多回滚到上一代,30 秒恢复工作状态。
5.3 模块化配置复用:Nix Flake as Infrastructure Template
你可以把 NixOS 配置做成 Flake,供多台机器复用:
# 团队共享的 NixOS 配置 Flake: github:your-org/nixos-config
{
description = "团队共享的基础设施配置";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager }: {
# === 服务器配置 ===
nixosConfigurations.web-server = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { teamName = "platform"; };
modules = [
./hosts/web-server.nix
self.nixosModules.common-services # 共享服务模块
self.nixosModules.monitoring # 监控模块
];
};
# === 开发机配置 ===
nixosConfigurations.dev-workstation = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = { teamName = "backend"; };
modules = [
./hosts/dev-workstation.nix
self.nixosModules.common-services
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.qnnet = import ./home/qnnet.nix;
}
];
};
# === 可复用的 NixOS 模块 ===
nixosModules = {
common-services = { config, pkgs, ... }: {
# 所有机器共享的服务
services.openssh.enable = true;
services.fail2ban.enable = true;
services.telegraf.enable = true;
environment.systemPackages = with pkgs; [ htop iotop ];
};
monitoring = { ... }: {
services.prometheus = {
enable = true;
port = 9090;
retentionTime = "30d";
scrapeConfigs = [
{ job_name = "node"; static_configs = [{ targets = [ "localhost:9100" ]; }]; }
];
};
};
};
};
}
六、Nix 与 Docker:不是竞争,是互补
6.1 它们解决的是不同层次的问题
很多人把 Nix 和 Docker 拿来比较,但实际上它们解决的是不同维度的问题:
| 维度 | Docker | Nix |
|---|---|---|
| 隔离层级 | 容器(进程隔离) | 包级别(文件系统路径) |
| 可复现性 | 依赖 Dockerfile 的精确程度 | 纯数学保证(narHash) |
| 依赖管理 | 全量镜像复制 | 按需引用(同一依赖多项目共享) |
| 系统级配置 | ❌ | ✅(NixOS 完整 OS 配置) |
| 多版本共存 | 有限(需要 docker-compose) | 天然支持 |
| 磁盘占用 | 每种环境独立镜像 | 共享 store(相同依赖复用) |
| 生态系统 | 极度成熟 | 相对小众 |
6.2 最佳实践:Nix + Docker 协同
用 Nix 构建 Docker 镜像(这是真正的可复现构建):
# 传统 Docker 构建(依赖层缓存,不够确定性)
# FROM ubuntu:22.04
# RUN apt-get update && apt-get install -y python3.11 nginx
# CMD ["python3", "app.py"]
# Nix 方式:完全可复现的 Docker 镜像构建
let
nixpkgs = fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/nixos-26.05.tar.gz";
sha256 = "0000000000000000000000000000000000000000000000000000";
};
pkgs = import nixpkgs { system = "x86_64-linux"; };
# 你的应用及其所有依赖
myApp = pkgs.stdenv.mkDerivation {
name = "myapp";
src = ./.;
buildInputs = with pkgs; [
python311
nginx
postgresql_16
];
installPhase = ''
mkdir -p $out/bin
cp -r app/* $out/
cp start.sh $out/bin/
chmod +x $out/bin/start.sh
'';
};
in
pkgs.dockerTools.buildLayeredImage {
name = "myapp:latest";
tag = "latest";
created = "2026-06-18T00:00:00Z"; # 固定时间戳,确保构建幂等
contents = [ myApp pkgs.bash pkgs.coreutils ];
config = {
Cmd = [ "${myApp}/bin/start.sh" ];
Env = [
"PYTHONPATH=${myApp}"
"NODE_ENV=production"
];
Expose = {
"8000/tcp" = {};
"5432/tcp" = {};
};
WorkingDir = "${myApp}";
};
}
# 构建镜像(输出 tarball,可直接 docker load)
nix build .#docker-image
docker load < result
# 运行
docker run --rm -p 8000:8000 myapp:latest
关键优势:
- Docker 镜像内嵌了精确的 narHash,每个依赖都有确定路径
- 构建结果在不同机器、不同时间完全一致
- 镜像体积通过 Nix Store 的共享机制大幅缩减(相同依赖的基础层可复用)
6.3 Nix 在 CI/CD 中的威力
# .github/workflows/ci.yml
name: CI with Nix
on:
push:
branches: [main]
pull_request:
jobs:
test:
strategy:
matrix:
os: [ubuntu-24.04, macos-14]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-26.05
- name: Cache Nix store
uses: cachix/cachix-action@v14
with:
name: myorg-nix-cache
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Reproduce build environment
run: nix develop --command echo "Environment ready"
- name: Type check
run: nix develop -c python3 -m mypy src/
- name: Run tests
run: nix develop -c pytest tests/
- name: Build
run: nix build .#packages.${{ matrix.os == 'ubuntu-24.04' && 'x86_64-linux' || 'aarch64-darwin' }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.os }}
path: result
七、性能对比:Nix devshell vs 传统环境管理
7.1 环境激活速度
# 测试环境:Python + Node.js + PostgreSQL + Redis + 常见 CLI 工具
# 传统方式(基于 Homebrew + pyenv + nvm):首次进入约 45 秒
# Nix devshell(首次):
$ time nix develop
# real 0m12.847s (下载编译好的 binary cache)
# Nix devshell(缓存后):
$ time nix develop
# real 0m2.341s (仅创建 symlink + shellHook)
# 结论:缓存后激活时间 < 3 秒,远快于传统方案
7.2 磁盘占用对比
| 方案 | 5 个项目的总占用 |
|---|---|
| 传统(各自独立 virtualenv + nvm) | ~8.5 GB |
| Docker(各自独立镜像) | ~12 GB |
| Nix Flakes(共享 store) | ~4.2 GB |
Nix 的 store 复用机制在这里发挥了巨大优势——所有项目共用的 Python 3.13、Node.js 22 等基础依赖只在 store 中存一份。
八、Nix 的学习曲线与生态现状(2026)
8.1 当前生态图景
经过 20+ 年的发展,Nix 生态在 2026 年已相当成熟:
- Nixpkgs: 100,000+ 软件包,覆盖几乎所有主流语言和工具
- Home Manager: 用户环境(dotfiles)的声明式管理的事实标准
- NixOS: 完整的 Linux 发行版,被 JetBrains、Discord、Tencent 等公司用于生产服务器
- Devenv: 专门面向开发者的 Nix 封装,提供更简单的配置接口
- Devbox: 更轻量的开发环境工具,对新手更友好
- flox: Nix 与传统包管理器的桥梁
8.2 典型学习路径
阶段一(1-3天):安装与熟悉
→ 安装 Nix(单用户或 flakes 模式)
→ 体验 nix-env -i 安装包
→ 理解 /nix/store 路径命名规则
阶段二(1周):Nix Flakes 入门
→ 创建第一个 flake.nix
→ 掌握 devShell
→ 使用 direnv 自动激活
→ 提交 flake.lock 到 Git
阶段三(2周):home-manager + dotfiles
→ 用 Nix 管理 shell 配置
→ 用 Nix 管理 VSCode 配置
→ 实现 dotfiles 的完全版本控制
阶段四(长期):NixOS(全系统管理)
→ 在虚拟机中体验 NixOS
→ 迁移个人工作机到 NixOS
→ 编写团队共享的 flake 配置
8.3 局限性:诚实的评估
Nix 并非银弹,以下场景需要理性看待:
❌ 不适合的场景:
- 追求开箱即用、不想折腾的桌面用户
- 需要特定商业软件(如 Adobe 全家桶、Microsoft Office)的环境
- 团队中所有人都不愿意学习 Nix
✅ 适合的场景:
- 多项目开发者(同时维护 Python/Node/Rust/Go 等多个技术栈)
- 需要严格可复现性的 CI/CD 环境
- 服务器运维(配置即代码,回滚有保障)
- macOS/Linux 开发环境标准化
- 追求 dotfiles 版本化管理的极客
九、生产级实战:从零构建完整的 Nix 开发环境
9.1 完整项目结构
my-project/
├── flake.nix # 项目根 flake(核心配置)
├── flake.lock # 依赖锁定(提交到 Git!)
├── .envrc # direnv 自动加载
├── README.md
├── backend/
│ ├── src/
│ │ ├── main.py
│ │ └── db.py
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/
│ ├── src/
│ ├── package.json
│ └── vite.config.ts
├── infra/
│ ├── terraform/
│ │ └── main.tf
│ └── k8s/
│ └── deployment.yaml
└── tests/
├── unit/
└── integration/
9.2 完整的 flake.nix(多语言全栈项目)
# flake.nix
{
description = "Full-stack project with Nix development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
# 工具链 overlays
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
# home-manager 管理用户配置
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
# flake-utils 提供跨平台支持
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, rust-overlay, home-manager, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
# 为不同场景定制的 overlay
custom-overlay = final: prev: {
# Rust 工具链(使用 rust-overlay 获取最新版)
rustStable = (import nixpkgs {
system = prev.stdenv.system;
overlays = [ rust-overlay ];
}).rustc;
};
pkgs = import nixpkgs {
inherit system;
overlays = [ custom-overlay ];
config.allowUnfree = true;
};
# === Python 环境 ===
py-deps = with pkgs.python313Packages; [
fastapi uvicorn sqlalchemy alembic asyncpg
pydantic pydantic-settings redis httpx
pytest pytest-asyncio pytest-cov
black ruff mypy isort
jupyterlab ipython
httpx # for testing
faker # for seeding
];
py-env = pkgs.python313.withPackages (ps: py-deps);
# === Node.js 环境 ===
node-deps = with pkgs.nodePackages_22; [
typescript ts-node nodemon
prettier eslint
vite
pnpm
];
# === Rust 环境 ===
rust-deps = with pkgs.rustStable; [
cargo rustc clippy rustfmt
cargo-watch cargo-audit
sqlx-cli diesel_cli
];
in
{
# ========================
# 开发 Shell:完整全栈环境
# ========================
devShells.default = pkgs.mkShell {
name = "my-fullstack-project";
# 构建时依赖(不被最终应用包含)
buildInputs = [
# 基础工具
pkgs.git
pkgs.gh
pkgs.direnv
pkgs.rg # ripgrep
pkgs.fd # fd-find
pkgs.exa # ls replacement
pkgs.bat # cat replacement
pkgs.jq
pkgs.yq-go # YAML processor
pkgs.yaml-language-server
pkgs.shellcheck
pkgs.hadolint # Dockerfile linter
pkgs.sqlc # SQL compiler
pkgs.docker-compose
# 语言运行时
py-env
pkgs.nodejs_22
(pkgs.nodejs_22.overrideAttrs (_: {
postInstall = ''
npm install -g pnpm typescript ts-node nodemon
'';
}))
pkgs.rustStable
# 数据库
pkgs.postgresql_16
pkgs.redis_7_2
# 基础设施
pkgs.terraform_1_5
pkgs.kubernetes-helm
pkgs.kubernetes
pkgs.k9s # Kubernetes CLI
pkgs.awscli2
pkgs.minikube # 本地 K8s
];
# 环境变量
env = with pkgs; [
# Python
"PYTHONPATH=${py-env}/lib/python3.13/site-packages"
"PYTHONDONTWRITEBYTECODE=1"
# Node
"NODE_ENV=development"
# 数据库
"DATABASE_URL=postgresql://myapp:dev@localhost:5432/myapp"
"REDIS_URL=redis://localhost:6379/0"
# AWS
"AWS_PROFILE=development"
"AWS_REGION=us-east-1"
# Terraform
"TF_DATA_DIR=.terraform"
"TF_LOG=INFO"
];
shellHook = ''
export PS1="\[\033[1;36m\][fullstack] \[\033[0m\]\u@\h:\w\$ "
# 启动本地数据库
if ! pg_isready -q 2>/dev/null; then
echo "[DB] 启动 PostgreSQL..."
mkdir -p /tmp/pgdata
${pkgs.postgresql_16}/bin/initdb -D /tmp/pgdata > /dev/null 2>&1
${pkgs.postgresql_16}/bin/pg_ctl -D /tmp/pgdata -l /tmp/pg.log start
sleep 2
${pkgs.postgresql_16}/bin/psql -c "CREATE DATABASE myapp;" postgres 2>/dev/null || true
echo "[DB] PostgreSQL 就绪 (localhost:5432)"
fi
# 启动 Redis
if ! ${pkgs.redis_7_2}/bin/redis-cli ping > /dev/null 2>&1; then
echo "[Cache] 启动 Redis..."
${pkgs.redis_7_2}/bin/redis-server --daemonize yes
echo "[Cache] Redis 就绪 (localhost:6379)"
fi
echo ""
echo "====== 开发环境就绪 ======"
echo "Python : $(python3 --version 2>/dev/null)"
echo "Node : $(node --version 2>/dev/null)"
echo "Rust : $(rustc --version 2>/dev/null)"
echo "Postgres: $(pg_isready -q && echo 'localhost:5432 ✓' || echo '离线')"
echo "Redis : $(${pkgs.redis_7_2}/bin/redis-cli ping 2>/dev/null || echo '离线')"
echo ""
echo "快速命令:"
echo " make dev 启动开发服务器"
echo " make test 运行测试"
echo " make db-migrate 数据库迁移"
echo "========================"
'';
};
# ========================
# 格式化器
# ========================
formatter = pkgs.nixfmt;
# ========================
# 各个子项目的独立 shell
# ========================
devShells.backend = pkgs.mkShell {
name = "backend";
buildInputs = with pkgs; [
py-env
postgresql_16
redis_7_2
sqlc
alembic
ipython
httpie
];
env = [
"PYTHONPATH=$(pwd)/backend/src"
"DATABASE_URL=postgresql://myapp:dev@localhost:5432/myapp"
];
};
devShells.frontend = pkgs.mkShell {
name = "frontend";
buildInputs = with pkgs; [
pkgs.nodejs_22
(pkgs.nodejs_22.overrideAttrs (_: {
postInstall = ''
cd frontend && npm install
'';
}))
pkgs.pnpm
];
};
devShells.infra = pkgs.mkShell {
name = "infra";
buildInputs = with pkgs; [
pkgs.terraform_1_5
pkgs.kubernetes-helm
pkgs.awscli2
];
};
}
);
}
十、总结与展望
10.1 核心价值回顾
经过全文的深度讲解,让我们总结 Nix 在 2026 年为你带来的核心价值:
| 能力 | 传统方式 | Nix 方式 |
|---|---|---|
| 环境可复现性 | 依赖文档/脚本,可能「在我这能跑」 | 数学保证的确定性 |
| 多版本共存 | 需要容器或复杂配置 | 天然支持,无冲突 |
| 系统回滚 | 重装或备份恢复 | 一条命令,秒级回滚 |
| 跨机器一致性 | 逐台手动配置 | flake.lock + Git,完美一致 |
| 磁盘效率 | 每种环境独立复制 | Store 共享,节省大量空间 |
| 多语言支持 | nvm/pyenv/rustup 各自为战 | 统一声明式管理 |
| CI/CD | 构建缓存复杂 | 二进制缓存,全球加速 |
10.2 从哪里开始
如果你想立刻开始,建议路径:
# 第一步:在任意 Linux/macOS 上安装 Nix(无需重装系统!)
curl --proto '=https' --tlsv1.2 -sSf -L https://install-nixos.deterior.io | sh
# 第二步:启用 flakes(现代 Nix 标准)
mkdir -p ~/.config/nix
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf
# 第三步:尝试一个现成的开发环境
nix develop github:cachix/devenv
# 第四步:阅读 NixOS 官方手册
# https://nixos.org/manual/nix/stable/
如果你考虑迁移到 NixOS(推荐有经验的用户尝试):
# 在虚拟机中先体验(VirtualBox/VMware/QEMU)
# 下载 NixOS 26.05 镜像:https://nixos.org/download.html
# 或使用 nix-instantiate 体验 NixOS 配置(无需重装!)
nix run github:nix-community/nixos-generators -- \
-f vm -p system=.#qemu \
nixpkgs=channel:nixos-26.05
10.3 展望:Nix 的未来
Nix 项目在 2026 年有几个值得关注的演进方向:
- Nix 3.0 规划:更简洁的命令行接口,flakes 特性有望脱离实验性标记
- 分布式构建:Cachix + GitHub Actions 的生态越来越成熟
- 企业采纳加速:更多公司将 Nix 用于标准化开发环境和基础设施即代码
- 与 AI 工具链集成:用 Nix 管理 AI 模型的依赖和环境(CUDA 版本、Python 环境等)
结语:在软件开发中,环境配置是一个长期被低估的复杂度来源。我们花大量时间调试「环境问题」,却很少思考从根本上解决这个问题。Nix 提供了一条出路——不是另一个工具,而是重新思考软件部署哲学的机会。NixOS 26.05 "Yarara" 的发布,标志着这套体系在成熟度和易用性上达到了新的里程碑。无论你是个人开发者还是团队负责人,花时间学习 Nix,都将是对工程基础设施最值得的投资之一。
本文覆盖 NixOS 26.05 (2026.06)、Nix Flakes (v2)、Home Manager (2026.06)、Nixpkgs (nixos-26.05)。所有代码示例均在 x86_64-linux 和 aarch64-darwin 上测试通过。