编程 NixOS 26.05 "Yarara" 深度实战:当声明式配置成为开发环境的终极形态——从 Nix Flakes 到 devshell、从原子化回滚到跨平台复现的完全指南(2026)

2026-06-18 21:27:15 +0800 CST views 4

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 的几个核心问题:

  1. 依赖版本锁定:Flakes 使用 flake.lock 文件精确锁定所有依赖的版本
  2. 规范化接口:每个 Flake 提供确定的 outputs 接口,不再需要理解复杂的 Nix 表达式语法
  3. 跨仓库引用:可以直接引用其他 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 checknix 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 拿来比较,但实际上它们解决的是不同维度的问题:

维度DockerNix
隔离层级容器(进程隔离)包级别(文件系统路径)
可复现性依赖 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 年有几个值得关注的演进方向:

  1. Nix 3.0 规划:更简洁的命令行接口,flakes 特性有望脱离实验性标记
  2. 分布式构建:Cachix + GitHub Actions 的生态越来越成熟
  3. 企业采纳加速:更多公司将 Nix 用于标准化开发环境和基础设施即代码
  4. 与 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 上测试通过。

推荐文章

使用Vue 3和Axios进行API数据交互
2024-11-18 22:31:21 +0800 CST
测试文章中文
2026-06-14 21:19:50 +0800 CST
mysql 计算附近的人
2024-11-18 13:51:11 +0800 CST
一个数字时钟的HTML
2024-11-19 07:46:53 +0800 CST
rangeSlider进度条滑块
2024-11-19 06:49:50 +0800 CST
windon安装beego框架记录
2024-11-19 09:55:33 +0800 CST
JavaScript 的模板字符串
2024-11-18 22:44:09 +0800 CST
Nginx 状态监控与日志分析
2024-11-19 09:36:18 +0800 CST
Vue3结合Driver.js实现新手指引功能
2024-11-19 08:46:50 +0800 CST
程序员茄子在线接单