NixOS 软件包管理实战指南:声明式与 Ad Hoc 两种包管理方式详解
2026/9/20 23:18:30 网站建设 项目流程
  • 包管理器
  • 操作系统

【免费下载链接】nixpkgs

Nix Packages collection & NixOS

项目地址:https://gitcode.com/GitHub_Trending/ni/nixpkgs
点击查看免费下载

本指南基于 NixOS 手册"Package Management"章节,系统讲解 NixOS 添加软件的两种包管理风格:以configuration.nix为载体的声明式管理,以及以nix-env命令为核心的临时(Ad Hoc)管理。读完本文,你将掌握environment.systemPackages选项的底层原理、nix-env的安装/升级/卸载/回滚操作,以及两者的适用场景与本质区别,并能在 Nixpkgs 仓库中追踪到对应实现源码。

NixOS 的两种软件包管理风格

在 NixOS 中,为系统添加软件包有两种截然不同的方式(见 package-mgmt.chapter.md):

  • 声明式(Declarative):在configuration.nix中声明你想要的软件包。每次执行nixos-rebuild,NixOS 都会确保你获得一组与配置规范完全一致的、可复现的二进制软件集。
  • 临时(Ad hoc):通过nix-env命令安装、升级和卸载软件包。这种方式允许混合使用不同 Nixpkgs 版本的软件包,而且它是非 root 用户唯一可用的方式。

两种方式对应手册中的两个独立小节:声明式软件包管理 与 临时软件包管理。下面分别深入讲解。

声明式软件包管理:一切由 configuration.nix 决定

声明式管理的核心是配置选项environment.systemPackages。例如,在/etc/nixos/configuration.nix中加入下面一行即可启用 Mozilla Thunderbird 邮件客户端:

{ environment.systemPackages = [ pkgs.thunderbird ]; }

其效果是:每当你运行nixos-rebuild switch,Thunderbird 包就会作为系统构建的一部分被编译或下载,并进入系统环境。要"卸载"某个包,只需把它从environment.systemPackages中移除,然后再次执行nixos-rebuild switch——系统会自动切换到不含该包的下一代配置。

底层实现:system-path 与 /run/current-system/sw

从源码层面看,该选项定义在 nixos/modules/config/system-path.nix:

environment.systemPackages = lib.mkOption { type = lib.types.listOf lib.types.package; default = [ ]; example = lib.literalExpression "[ pkgs.firefox pkgs.thunderbird ]"; description = '' The set of packages that appear in /run/current-system/sw. These packages are automatically available to all users, and are automatically updated every time you rebuild the system configuration. ''; };

关键信息有两点:

  1. 该选项类型是"package 列表",默认值为空,示例写法[ pkgs.firefox pkgs.thunderbird ]与手册中的用法完全一致。
  2. 所有列入该选项的包最终都会出现在/run/current-system/sw目录,对所有用户自动可用,并且在每次系统重建时自动更新——这正是它与"默认 profile 安装"(/nix/var/nix/profiles/default)的本质区别。

在配置装配阶段(system-path.nix),systemPackages还会自动叠加内置的corePackagesdefaultPackages

environment.systemPackages = config.environment.corePackages ++ config.environment.defaultPackages;

随后整个包集被交给pkgs.buildEnv构建成system.path(即/run/current-system/sw),并按 pathsToLink 的默认设置将/bin/sbin/etc/xdg/lib等目录软链接进系统环境(见 system-path.nix)。这就是"重启后软件对所有人可见"的实现机制。

注意事项:并非所有包都能只靠 systemPackages 工作

手册特别提醒:某些软件包还需要额外的全局配置,例如 D-Bus 服务或 systemd 服务的注册。仅仅把它们加入environment.systemPackages可能不够。建议先在 NixOS 模块选项列表 中查一下该软件是否已存在对应的 NixOS 模块(如programs.firefoxservices.*等)。这类带模块的包通常应该用模块启用,以获得完整的集成。

查询可用软件包

你可以用nix-env列出 Nixpkgs 中所有可用软件包及其描述:

$ nix-env -qaP '*' --description nixos.firefox firefox-23.0 Mozilla Firefox - the browser, reloaded ...

输出中第一列是属性名(attribute name),例如nixos.thunderbird。请注意前缀语义:

  • nixos前缀表示从nixos渠道获取该包,它只在命令行工具中有效
  • 在声明式配置(configuration.nix)中,必须改用pkgs前缀(pkgs是配置文件中的内置变量),写成pkgs.thunderbird

临时(Ad Hoc)软件包管理:用 nix-env 即时操作

对于不需要声明进配置、只想临时使用的软件,nix-env是更灵活的手段。它也非 root 用户唯一可用的安装方式

安装软件包

安装 Mozilla Thunderbird:

$ nix-env -iA nixos.thunderbird

这里的关键在于-A标志:

  • -A时,按属性名(attribute name)精确指定软件包;
  • 不带-A时,则按包名(package name,例如thunderbird)在所有 Nix 包中做匹配。这种方式更慢(需要对全部包做匹配),而且存在歧义风险——若有多个同名包会无法确定装哪个。

安装位置与可见性

nix-env将软件装进 Nix profile,安装位置取决于执行用户身份:

执行身份安装位置可见性
root/nix/var/nix/profiles/default系统所有用户可见
普通用户/nix/var/nix/profiles/per-user/<用户名>/profile仅该用户可见

升级软件包

临时安装的包来自 NixOS 渠道。典型的升级流程是先把 NixOS 渠道更新到最新:

$ nix-channel --update nixos

然后重新执行nix-env -i(重新安装目标包)。此过程不会影响 profile 中的其他包——这是与声明式管理的决定性差异:声明式下执行nixos-rebuild switch会把所有包都同步到 NixOS 渠道中的当前版本,而nix-env是"各装各的"。

若想一次性升级 profile 中所有有新版可用的包,可执行:

$ nix-env -u '*'

卸载与回滚

卸载某个包用-e标志(按包名指定):

$ nix-env -e thunderbird

最后,如果某次nix-env操作结果不理想,可以一键回滚:

$ nix-env --rollback

nix-env还支持更多标志,完整说明见nix-env(1)手册页。由于 Nix 的 store 天然保留历史世代(generations),回滚几乎是即时且无损的。

软件包不够用怎么办:添加自定义软件包

当 Nixpkgs 中没有你需要的软件时,手册提供了两条出路(详见 添加自定义软件包):用 Nix 打包,或使用上游预编译二进制。由于 NixOS 的文件系统布局与主流发行版差异较大,从源码构建往往比使用预编译可执行文件更容易成功

在 Nixpkgs 树内打包(in-tree)

克隆 Nixpkgs 仓库,把新包加入自己的克隆,可选地向主仓库提交补丁或 Pull Request。完成后把它加入environment.systemPackages

{ environment.systemPackages = [ pkgs.my-package ]; }

重建时用-I指定自己的 Nixpkgs 树:

# nixos-rebuild switch -I nixpkgs=/path/to/my/nixpkgs

在 configuration.nix 中直接打包(out-of-tree)

也可以直接在configuration.nix里用stdenv.mkDerivation定义构建,例如构建 GNU Hello:

{ environment.systemPackages = let my-hello = pkgs.stdenv.mkDerivation (finalAttrs: { pname = "hello"; version = "2.8"; src = pkgs.fetchurl { url = "mirror://gnu/hello/hello-${finalAttrs.version}.tar.gz"; hash = "sha256-5rd/gffPfa761Kn1tl3myunD8TuM+66oy1O7XqVGDXM="; }; }); in [ my-hello ]; }

更优雅的做法是把包定义抽到独立文件(如my-hello.nix),再通过callPackage引入:

{ environment.systemPackages = [ (pkgs.callPackage ./my-hello.nix { }) ]; }

其中my-hello.nix的内容为:

{ stdenv, fetchurl }: # declare dependencies as arguments stdenv.mkDerivation (finalAttrs: { pname = "hello"; version = "2.8"; src = fetchurl { url = "mirror://gnu/hello/hello-${finalAttrs.version}.tar.gz"; hash = "sha256-5rd/gffPfa761Kn1tl3myunD8TuM+66oy1O7XqVGDXM="; }; })

这样还可以在不重建整个系统的情况下单独测试该包:

$ nix-build -E 'with import <nixpkgs> { }; callPackage ./my-hello.nix { }' $ ./result/bin/hello Hello, world!

预编译可执行文件的两个例外

大多数预编译可执行文件无法直接在 NixOS 上运行,但有两类例外:Flatpak 与 AppImage。AppImage 在 NixOS 上可以"原样"运行,前提是先启用支持:

{ programs.appimage.enable = true; programs.appimage.binfmt = true; }

之后可直接运行foo.appimage,或用appimage-run foo.appimage。若缺少共享库,可通过覆盖补充:

{ programs.appimage.package = pkgs.appimage-run.override { extraPkgs = pkgs: [ # missing libraries here, e.g.: `pkgs.libepoxy` ]; }; }

其他预编译程序则需用autoPatchelfHookbuildFHSEnv等工具配合 Nix 打包才能运行,过程复杂,往往不如直接源码构建。

小结:如何选择包管理方式

维度声明式(environment.systemPackages临时(nix-env
配置载体/etc/nixos/configuration.nix命令行
生效方式nixos-rebuild switch即时生效
版本一致性每次重建时整组同步到渠道当前版本各包独立,可混用不同版本
可见性所有用户可见(装入/run/current-system/swroot 装全局,普通用户仅自己可见
非 root 用户不可用唯一选择
卸载/回滚从配置中移除后重建nix-env -e/nix-env --rollback

两种方式各有用武之地:追求可复现、可审计的系统配置,优先声明式;临时试用、非 root 环境、希望混用不同版本,则使用nix-env。掌握二者并结合 添加自定义软件包 与 自定义软件包 中的技巧,即可在 NixOS 上从容应对绝大多数软件安装场景。

  • 包管理器
  • 操作系统

【免费下载链接】nixpkgs

Nix Packages collection & NixOS

项目地址:https://gitcode.com/GitHub_Trending/ni/nixpkgs
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询