在非 NixOS 下使用 Nix 的 Home-Manager 的诸多坑

Nix 是 NixOS 的包管理器,而在非 NixOS 下也可以使用它,也即它具有跨发行版的特性,理论上能够非常方便地在各发行版上搭建相同或相似的环境配置。

理想很美好,但当我真的入门了 Nix 语言(顺手重写了 NixOS 中文社区的《Nix 语言快速入门》),且在 Debian 上实践了 Home-manager 之后,我踩的坑越来越多。

根据我在 Arch 与 Debian 下安装使用 Nix 的经历,目前发现的坑包括但不限于:

安装 Nix

(基本已解决)

无论是使用 Nix 官方的安装脚本,还是 Arch Linux 打包好的 Nix,都会出现一旦安装失败就很难清理干净的问题(不清理干净是无法再次尝试安装的),尤其是因为这个过程涉及大量创建更改系统的用户与组的配置,如果没有做好系统快照的话,回退起来相当麻烦。

目前我已知的唯一解是使用基于 Determinate nix-installer 的实验性的 NixOS/experimental-nix-installer

所谓 Determinate Nix 是基于 Nix 的一个项目,它的安装器比较稳健,本来也支持安装上游的 Nix,但据 Dropping upstream Nix from Determinate Nix Installer,对上游 Nix 的安装支持将会被取消;而 Nix 社区基于它的复刻 NixOS/experimental-nix-installer 最近才报过一次 404,也不够稳定,所以这个问题暂时只能算做基本解决。

GPU 加速

(基本解决?)

由 Home-manager 安装的包无法直接调用 opengl、vulkan 以便利用 GPU 加速,而是需要 NixGL,但 NixGL 以 opengl 作为后端时性能很差,若以 Nvidia 等作为后端则必须指定 --impure(会降低构建的可复现性),并且我实际测试的时候即使指定了 --impure 也还是会报错(可能是我配置的问题,暂未解决)。

更新:

25.05 实测无效,但是从 25.05 升到 25.11 就可以了。

更新:但是 Hyprland + Nvidia 在这个配置下还是无法启动,报错 EGL: failed to initialize a platform display;也有可能是 Nvidia 驱动本身有问题。已经试过 hyprland wiki 给出的针对 Nvidia 的说明。

PAM 验证

(暂未解决)

使用 Home-manager 安装的程序在调用 PAM 时会失败,典型的是锁屏程序例如 swaylock,如果是用 Home-manager 安装的,即使密码正确也无法解锁。这个问题似乎可以得到解决,但仍未正式实现,见相关讨论

对由 DM 启动的图形环境启用 Nix

(已解决)

安装 Nix 之后,在 /etc/zshrc 等 shell 的配置中会有:

if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
  . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
fi

如果你的图形环境不是从 tty 启动而是从 DM 启动的,可能不会读取这个配置,导致由 Nix 安装的应用与二进制文件仅在 Shell 中可用,而在应用启动器中不可见或不可用。

这种情况下,你需要以某种方法把它加入到图形环境的启动流程中。例如,对于 KDE Plasma,可以创建一个 ~/.config/plasma-workspace/env/00nix.sh 并填入上述配置。

Joplin 调用的外部编辑器无法正常访问 /usr

(暂未解决)

Joplin 是一个主要基于 Markdown 的跨平台自由开源笔记软件。实测使用 Home-manager 安装 joplin-desktop,在 Joplin 中将 Emacs 作为外部编辑器打开时,Emacs 会报错:Error (use-package): tex/:catch: Searching for program: No such or directory, /usr/bin/zsh(类似地,在尝试复制时也找不到 wl-copy)。

我的 Emacs 配置里没提到 zsh,所以这个大概是读取了用户的默认 Shell;并且在我的系统中并未使用 Home-manager 来安装 Zsh。然而,在系统中 /usr/bin/zsh 是实际存在的。

经排查发现,Joplin 在调用外部编辑器时,/usr 作为挂载点实际指向 /nix/store/<HASH>-joplin-desktop-<VERSION>-fhsenv-rootfs/usr(让 Joplin 调用外部脚本,由 cat /proc/self/mountinfo 即可得知此信息),而这个 /usr 中不含有原本的 /usr/bin/zsh,却含有大量指向 /nix/store 的软链接。

目前我已改用 AppImage 版本的 Joplin,此问题不再出现,可见这确实是由 Home-manager 安装的 Joplin 的问题。


总之,有过类似想法(即,用 Nix 代替系统包管理器,来安装除了 kernel、bootloader 这种底层的包以外的几乎所有包)的朋友们建议慎重考虑在非 NixOS 上使用 Nix。

另:我注意到 Guix 官网 似乎比较强调 Guix 在非 Guix 发行版上的使用:

您可在任意 GNU/Linux 发行版上安装 Guix,与原系统包管理器并行不悖,互不冲突。

因此,Guix 可能不存在 Nix 的上述这些问题?

3 个赞

之前看 guix 二进制缓存要么缺要么旧,用起来可能比较蛋疼,而且必须要守护进程,就没入坑

nix 我只用来装 cli 二进制,恰好避开主帖的大部分坑(

如果不是非要声明式配置的话,感觉 gui 场景用 distrobox 之类的工具凑合下可能更靠谱

1 个赞

还有一个应用场景我觉得挺合适的,就是安装字体图标这些资源文件。

二进制缓存我倒不是很在意了,反正无论是 Arch 还是 Nix 实际用起来都避不开编译。守护进程只要不是太占资源都好说。

我倒是知道 distrobox,只是还没有尝试过。不知道 distrobox 有没有其它的坑点。

哈哈,仅限于能用,之前折腾 steamdeck 的时候测过,这些花里胡哨的都不如 flatpak 靠谱

目前我已知的唯一解是使用基于 Determinate nix-installer 的实验性的 NixOS/experimental-nix-installer

lix 也是一个 alternative

guix 本质上就是 nix。它的后端的编译 daemon 和 nix 用的是同一个 daemon,只不过 derivation 不用 nix-lang 来写而是用 lisp 来写,所以 nix 有的坑 guix 都少不了。而且 nix 还自带了很多非自由软件和驱动,guix 官方拒绝维护非自由软件,你只能用非官方维护的第三方源。

由 Home-manager 安装的包无法直接调用 opengl、vulkan 以便利用 GPU 加速,而是需要 NixGL,但 NixGL 以 opengl 作为后端时性能很差,若以 Nvidia 等作为后端则必须指定 --impure

没试过 standalone nix 安装非 N 卡驱动的图形程序,网上搜了一下,似乎解决方案挺多的,比如这个不行吗? GitHub - numtide/nix-gl-host: Run OpenGL/Cuda programs built with Nix, on all Linux distributions. 以及为什么要 --impure?直接 allow-unfree 不就行了?

1 个赞

不加 --impure 的话,在 nixGL.defaultWrapper = "nvidia"; 的时候报错如下:

       error: attribute 'currentTime' missing
       at «github:nix-community/nixGL/a8e1ce7»/nixGL.nix:224:18:
          223|           # Add an impure parameter to force the rebuild on each access.
          224|           time = builtins.currentTime;
             |                  ^
          225|           preferLocalBuild = true;

加了 --impure 就变成

warning: Git tree '/home/dot/lilydots' has uncommitted changes
error:
       … while calling the 'derivationStrict' builtin
         at «nix-internal»/derivation-internal.nix:37:12:
           36|
           37|   strict = derivationStrict drvAttrs;
             |            ^
           38|

       … while evaluating derivation 'home-manager-generation'
         whose name attribute is located at /nix/store/ddv2lp5l4xd23l7pp7arr7755iqnplxx-source/pkgs/stdenv/generic/make-derivation.nix:480:13

       … while evaluating attribute 'buildCommand' of derivation 'home-manager-generation'
         at /nix/store/ddv2lp5l4xd23l7pp7arr7755iqnplxx-source/pkgs/build-support/trivial-builders/default.nix:80:17:
           79|         enableParallelBuilding = true;
           80|         inherit buildCommand name;
             |                 ^
           81|         passAsFile = [ "buildCommand" ] ++ (derivationArgs.passAsFile or [ ]);

       … while evaluating the option `home.activation.installPackages.data':

       … while evaluating definitions from `/nix/store/qnrjn13sd2dg73z441j7kk6yp99w8ab9-source/modules/home-environment.nix':

       (stack trace truncated; use '--show-trace' to show the full, detailed trace)

       error: cannot coerce null to a string: null

从这个报错来看,allow-unfree 应该不会有什么作用,也不知道应该加在哪。实际上我是用 apt 安装的 nvidia-open。显卡驱动这种比较硬件底层的包我是不会用 home-manager 来安装的,毕竟 home-manager 是针对用户层次的。

谢谢推荐。我当初使用 NixGL 也是根据报错搜过去的;至于这个项目我可以尝试一下。

——更新:我发现我只会用 Home-manager,不知道 nix-gl-host 要怎么用。不过它的 README 倒是有这么一段来说明它和 NixGL 的差异:

This works has drawn inspiration from NixGL. NixGL solves the same issue with a different approach. Instead of re-using the host GL libraries, it uses the Nixpkgs-provided ones to wrap the OpenGL program. With this approach, the graphic drivers are also distributed through Nix. The Nix closure is fully contained. It’s a safer approach as you’re less likely to stumble upon any DSO ABI incompatibility.

However, this explicit approach comes with tradeoff: you need to build one Nix closure for each graphic driver you plan to support. That also includes versions of the driver. This makes the deployment more complex: for each machine, you need to figure out which GPU-specific Nix closure to use.

How we see it, NixGL is best adapted for closed environment where the target hosts are under the author’s control. NixGlHost is best adapted for open environments where the author doesn’t control who is going to use their program.

更新:关于 GPU,home-manager 最近才合并了一个据说比 NixGL 更好的方案,等我有空尝试一下。见 home-manager/docs/manual/usage/gpu-non-nixos.md at master · nix-community/home-manager · GitHub

更新:我发现我只会用 Home-manager,不知道 nix-gl-host 要怎么用

我没有环境 test,不过 nixglhost 应该是这么用的。你在你的 flake 里面把 nixglhost 加入到你的 input 里面,然后在 home.packages 里面加上这个就可以了 nixglhost.defaultPackage.x86_64-linux。然后就可以调用 nixglhost wrapper 了。能不能用我就不知道了。

1 个赞

谢谢,nixglhost 可以用了,但是发生了很奇怪的事:

  • 我无法复现 NixGL 性能很差的问题了,不记得是哪个图形应用导致非常卡顿,所以我不确定 nixglhost 是不是真的性能足够好,还是说我测试的场景无法判断出图形性能。
  • Hyprland 仍然跑不起来。(据 Hyprland wiki,它有特定于 Nvidia 相关的专属要求,不过即使做了这些也无法启动;生成的错误日志还是空的)

有空还是打算再看看 https://github.com/nix-community/home-manager/blob/master/docs/manual/usage/gpu-non-nixos.md

用 standalone nix 在非 nixOS 安装桌面环境,听起来感觉就是会踩很大坑的场景 :joy::joy:

我个人感觉 nix和guix 在搭建需要强审计和安全的服务器情况下比较合适,用作个人桌面太折腾了, 我用了几年guix,退回 debian 了,guix 的强项在用作桌面时变成了不小的负担,太消耗脑细胞。

如果你的图形环境不是从 tty 启动而是从 DM 启动的,可能不会读取这个配置,导致由 Nix 安装的应用与二进制文件仅在 Shell 中可用,而在应用启动器中不可见或不可用。

这个其实是一个 wayland 没有一统一标准产生的问题。x11 由于 xsession、xinit、xprofile 的存在,DE (可能)是会去读相应的文件来执行用户脚本。但现在主流 wayland 相关启动器都没有这一个过程,需要只能自己去整。

解决方法有几种,

  • 用 agreety 这种能自定义启动脚本的,自己搞个类似于 xsesion 这样的执行逻辑
  • 登陆时会涉及到 pam,把一些环境变量通过 pam 拉进来,比如 pam_env 这样的模块
  • 启动器查找和执行启动 DE 的原理是在特定目录下找 desktop 文件,去跑里面的 Exec,你可以直接改里面 Exec 换成自己包装的脚本

目前我已改用 AppImage 版本的 Joplin,此问题不再出现,可见这确实是由 Home-manager 安装的 Joplin 的问题。

这个我觉得是打包的问题,看起来像是为了打包时省事用了 fhs,导致运行时环境不是你实际的系统。可以去提个 issue。毕竟有些 GUI 包难打,图省事会这样。 此外,因为 nixpkgs 打包基本都是封闭的,尽可能不去依赖运行时的环境,也尽量不假定用户的运行环境。打包时很多路径都会被 patch 掉。如果是比较固定的执行文件会直接换成 nix/store 里的,但你这里的情况是运行时路径,应该是打包本身导致的。

因此,Guix 可能不存在 Nix 的上述这些问题?

这得看包是怎么打的,单独说包管理本身其实两者都不会影响。关键是看你打出来的包怎么工作。

由 Home-manager 安装的包无法直接调用 opengl、vulkan 以便利用 GPU 加速,而是需要 NixGL,但 NixGL 以 opengl 作为后端时性能很差,若以 Nvidia 等作为后端则必须指定 --impure(会降低构建的可复现性),并且我实际测试的时候即使指定了 --impure 也还是会报错(可能是我配置的问题,暂未解决)。

这个其实很难搞。因为 hm 有自己的依赖树,但 cuda 那些是分内核模块及其对应用户空间库,和一个开发调用的库(好像)。hm/nix 可以帮你搞定调用时的最后一个,但这个库要怎么去链你系统里的库呢?就像前面讨论的,尽量不假定用户运行环境,所以很难搞。而前两个本身就要匹配着安装,又有很多坑。

使用 Home-manager 安装的程序在调用 PAM 时会失败,典型的是锁屏程序例如 swaylock,如果是用 Home-manager 安装的,即使密码正确也无法解锁。

具体得看是什么问题。比如 pam 本身需要配置允许进程使用哪些模块来鉴权(nixos 默认后备能让 wayland 的这些工作,x11 则需要自己配置;其它系统可能也需要一些配置)。此外还有种经典的调用库和服务/内核模块不兼容的,这种说实话难搞哦。

2 个赞

我的 home manager 也是负责安装 cli 工具,管理配置文件。 项目上用 flake ,可以引入 nix 管理的库,创建一个干净的环境 其余再深一点就不好搞了。

1 个赞

我主要是是在docker里面用,快速恢复一套开发环境出来,其他的东西对我似乎不是很重要了lol

一直想尝试用 Nix 取代掉 Mac 上的 Homebrew。

  1. Home Manager 也是利用 Nixpkgs 来安装软件的吧?这是否意味着用 Nixpkgs 安装这些软件也有同样的问题?
  2. 我是否可以在使用 Nix 的同时通过手动/Homwbrew Casks 方法安装另一部分软件?

我就是用 nix-darwin 主要安装 cli,然后 gui app 用 Homebrew cask。

  1. Home Manager 也是利用 Nixpkgs 来安装软件的吧?这是否意味着用 Nixpkgs 安装这些软件也有同样的问题?

是的。但是这种问题基本不会在 mac 上有。因为 nix 在 mac 上基本只有 cli,没有 gui app。

我是否可以在使用 Nix 的同时通过手动/Homwbrew Casks 方法安装另一部分软件?

你可以用 nix 来声明式的管理你的 Homebrew 包。在你的 nix 配置里写你想要安装什么 Homebrew 的 formula cask tape 啥的。你还可以用 nix 来声明式安装 mac app store 里的包。

1 个赞

不太清楚,不过好像 Nix 在 macOS 上打包了带 GUI 版本的 Emacs(还包括了一种为 macOS 特化的变体——emacs-macport)。

话说应该安装哪一种 Emacs?(如果要带 GUI 的话,否则可以考虑 emacs-nox 或者 Homebrew 的 emacs Formula)

  • Nix 的 emacs
  • Nix 的 emacs-macport
  • Homebrew 的 emacs-app Cask

Flake 的 lockfile 能否记录 Homwbrew 的版本?毕竟 Nix 的“可复现性”要求还是需要这一条的。

我用的是这个: emacs-app Cask。主要是免去了手动编译和编译依赖。emacs-mac 已经缺乏维护了,我觉得没有 emacs-plus 好用。emacs-app cask 等价于 emacs-plus。

Flake 的 lockfile 能否记录 Homwbrew 的版本?毕竟 Nix 的“可复现性”要求还是需要这一条的。

想啥呢,Homebrew 是滚动更新的,锁不了 homebrew 版本而且没有任何意义,因为官方只提供最新版本的二进制包,老版本都直接从服务器里清除掉的。nix 管理 homebrew 的方法很简单的,其实就是在 activation script 里面调用根据你的声明式配置生成的 brew bundle file 来调用 brew bundle 而已。

我也在 Arch 上使用 Nix,我发现 Home Manager 25.11 起内置了 GitHub - exzombie/non-nixos-gpu: GPU driver setup for Nix on non-NixOS Linux systems ,在我添加 targets.genericLinux.enable = true; 这个参数并重新部署时,提示我开启 non-nixos-gpu.service 这个服务,启动之后似乎就没问题了(不需要 NixGL),不知道楼主有没有尝试过?

1 个赞

原来是 25.11 才内置的,其实我在上面已经注意到了,我说怎么试了没用。感谢。

更新:但是 Hyprland + Nvidia 在这个配置下还是无法启动。已经试过 hyprland wiki 给出的针对 Nvidia 的说明。