这是一篇持续更新的踩坑笔记,记录我在 Guix System 上遇到的问题、原因分析和解决办法。

1. bash 的 PATH 被 search path 机制重置,codex 注入的 apply_patch 也跟着失踪

现象

home.scm 里用 home-environment-variables-service-type 定义的 PATH(比如 $HOME/.local/bin:$PATH),对 bash 压根不生效:

(simple-service 'local-bin-path
 home-environment-variables-service-type
 '(("PATH" . "$HOME/.local/bin:$PATH")))

就是这段,bash 表示没见过。

更麻烦的是 codex:它会把 apply_patch 之类的工具注入 PATH 来暴露给 agent,而它执行命令的 functions.exec_command 默认用 login bash。search path 机制顺手把 codex 刚注入的那段 PATH 清理干净,apply_patch 等重要工具当场找不到。

原因

Guix 的 search path 机制重置 PATH 的时机或方法可能不太对:它在 profile / 会话加载时重新生成 PATH,把已有的 PATH 覆盖掉——不管那是你写在 home.scm 里的,还是 codex 刚刚塞进去的。

解决办法

正经解法还不知道,目前只能绕。我干脆换 fish 了(:

  • bash:在 .profile.bashrc 之类的文件里补 export PATH=...,注意要写在 search path 触发之后。
  • codex:告诉 codex,exec_command 必须加上 login: false

2. 环境变量带着 /gnu/store 漏进 flatpak 沙盒

distrobox 同理。

现象

Guix System 有一堆环境变量的值本身就是 /gnu/store/... 路径,它们跟着进程环境漏进 flatpak 沙盒,而沙盒里看不到 /run/current-system//gnu/store/

比如 SSL_CERT_FILE

❯ set -S SSL_CERT_FILE
SSL_CERT_FILE: 设定在 global 作用域,导出,包含 1 个元素
$SSL_CERT_FILE[1]: |/run/current-system/profile/etc/ssl/certs/ca-certificates.crt|

❯ ls -al /run/current-system/profile/etc/ssl/certs/ca-certificates.crt
lrwxrwxrwx 1 root root 99 1970年 1月 1日 /run/current-system/profile/etc/ssl/certs/ca-certificates.crt -> /gnu/store/5q06g15na23kmjy2c2hbwxynpm44vpvq-ca-certificate-bundle/etc/ssl/certs/ca-certificates.crt

变量进去了,文件却看不到,于是 SSL 报错,表现就是没有网络。

VDPAU_DRIVER_PATH 同理:

❯ set -S VDPAU_DRIVER_PATH
VDPAU_DRIVER_PATH: 设定在 global 作用域,导出, 路径变量,包含 1 个元素
$VDPAU_DRIVER_PATH[1]: |/home/sorubedo/.guix-home/profile/lib/vdpau|

❯ ls -al /home/sorubedo/.guix-home/profile/lib/vdpau/
libvdpau_nvidia.so -> /gnu/store/sxwdaplgifxfhdv8rhv66ppyrq9nrxij-nvda-610.57/lib/vdpau/libvdpau_nvidia.so*
libvdpau_trace.so -> /gnu/store/hc832ii502wxdg6h4kdcmr7ysn9b1ix6-libvdpau-1.5/lib/vdpau/libvdpau_trace.so*

目录里全是指向 /gnu/store/ 的符号链接。

原因

宿主的环境变量透传进了沙盒,值却指向沙盒里不存在的 /gnu/store 路径。

放行 /gnu/store 之后的衍生问题

我忽视沙盒安全性,给 flatpak 添加了这些目录的权限。SSL 是解决了,但原本「看得到环境变量、找不到文件、于是安全回退默认」从而正常的现象,反倒因为找到了文件而不正常了。

比如用 PrismLauncher 玩 MC:它原本找不到 /gnu/store 里的图形驱动文件,从而回退到沙盒内自带的运行时,正常游玩;对它开放 /gnu/store 后,反而驱动对不上,导致 MC 启动失败。

解决办法

显式 unset 掉这些环境变量(用 flatpak override --user --unset-env=名字),变量直接不进沙盒,各软件一律用沙盒自带的默认值。这样也就没必要给沙盒开 /gnu/store 的权限了。

我这里的 flatpak override --user --show,重点是这一长串 unset-environment

❯ flatpak override --user --show
[Context]
filesystems=~/Develop;xdg-templates;xdg-config/gtk-4.0:ro;xdg-videos;~/.icons:ro;xdg-public-share;xdg-pictures;xdg-download;xdg-music;xdg-config/gtk-3.0:ro;xdg-documents;~/.themes:ro;~/Projects;~/VMS
unset-environment=LIBVA_DRIVERS_PATH;OBS_PLUGINS_DATA_DIRECTORY;VK_ADD_IMPLICIT_LAYER_PATH;DICPATH;GBM_BACKENDS_PATH;BASH_LOADABLES_PATH;UDEV_HWDB_PATH;GTK_DATA_PREFIX;GUIX_GDK_PIXBUF_MODULE_FILES;INFOPATH;FLATPAK_BINARY;TERMINFO;GIT_SSL_CAINFO;OBS_PLUGINS_DIRECTORY;VDPAU_DRIVER_PATH;P11_MODULE_PATH;MANPATH;GUIX_GTK3_PATH;GUIX_LOCPATH;GUILE_LOAD_COMPILED_PATH;GHOSTTY_RESOURCES_DIR;GUIX_LUA_CPATH;GIT_EXEC_PATH;LINUX_MODULE_DIRECTORY;CURL_CA_BUNDLE;SSL_CERT_DIR;TERMINFO_DIRS;GSETTINGS_SCHEMA_DIR;SSL_CERT_FILE;GUIX_GTK3_IM_MODULE_FILE;GUILE_LOAD_PATH;GHOSTTY_BIN_DIR;FCITX_ADDON_DIRS;GUIX_LUA_PATH;GUILE_EXTENSIONS_PATH

[Environment] 段会把同一批变量再设成空值,作用是一样的,这里就不重复贴了。

3. flatpak.sh 不生效,启动器发现不了 flatpak 应用

现象

装好 flatpak 后,~/.guix-home/profile/etc/profile.d/flatpak.sh 不会生效。少了它设置的那几个变量,启动器就发现不了 flatpak 应用。

原因

flatpak.sh 走的是 profile.d 那套机制,但没人会去 source 它,所以脚本里往 XDG_DATA_DIRS 里加的 flatpak exports/share 路径从来没进过环境。

解决办法

自己在 home 里把这些变量补上(home.scm 里的 flatpak-environment):

(simple-service 'flatpak-environment
 home-environment-variables-service-type
 '(("XDG_DATA_DIRS" . "$HOME/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:$XDG_DATA_DIRS")))

4. home 侧 nvidia 驱动不生效,仍然链 mesa

驱动装是装了,就是不用。

现象

系统侧用 nonguix 的 nvidia 变换装驱动很方便:

((nonguix-transformation-nvidia #:driver nvda-new-feature
                                #:open-source-kernel-module? #t)
 %base-os)

但 home 侧的软件还是只链 mesa——nvidia 驱动和库明明在,就是不用。

原因

guix home reconfigureguix system reconfigure两次独立的求值

系统侧的变换只作用于那份 operating-system 的 packages / services,管不到 home 那份 home-environment;home 侧的包在求值时从没被替换过 mesa,于是照旧链 mesa。

解决办法

home 侧也要替换一遍:用 replace-mesa 包住 home 的 packages,或者干脆把整个 home-environment 包起来。

参考配置(整个 home 一起包):

(replace-mesa (home-environment
                (packages (list nvda-new-feature ...))
                (services (append (list ...) %base-home-services)))
              #:driver nvda-new-feature)

5. greetd 接管全部 tty 且删掉 login 服务模块:登录只剩 bash,锁屏无法解锁

现象

按手册把 tty1-6 全部改成 greetd、去掉自带的 login 服务模块和 tty1-6 原本的 mingetty 服务模块后:

  1. greetd 的 agetty 不管你登录 shell 配的是什么,登录进去只有 bash。
  2. 锁屏解不开。

原因

  1. agetty 那一层不读你在系统里配的登录 shell,所以只给 bash。
  2. login 服务模块包含 /etc/pam.d/login。greetd 用的是自己的 /etc/pam.d/greetd,但一定会有别的软件依赖 login,比如 Noctalia shell 的锁屏就用它,没有就解锁不了。

解决办法

只去掉 tty1 的 mingetty,改为 greetd + 自己喜欢的 greeter(我用 tuigreet);tty2-tty6 保留原本的 mingetty,并保留 login 服务模块。

参考配置:

;; greetd 使用 tty1,其余终端由 mingetty 提供登录提示。
(define tuigreet-command
  #~(string-append #$tuigreet
                   "/bin/tuigreet"
                   " --time"
                   " --remember"
                   " --asterisks"
                   " --power-shutdown 'loginctl poweroff'"
                   " --power-reboot 'loginctl reboot'"
                   " --cmd 'niri --session'"))

(define greetd-service
  (service greetd-service-type
           (greetd-configuration (terminals (list (greetd-terminal-configuration
                                                   (terminal-vt "1")
                                                   (terminal-switch #t)
                                                   (default-session-command
                                                    tuigreet-command)))))))

(define mingetty-services
  (map (lambda (tty)
         (service mingetty-service-type
                  (mingetty-configuration (tty tty))))
       '("tty2" "tty3" "tty4" "tty5" "tty6")))
(services
 (append (list greetd-service ...)
         mingetty-services
         (modify-services %base-services
           ;; 保留 login-service-type,供 Noctalia 生成 /etc/pam.d/login。
           (delete mingetty-service-type))))

6. home-niri-service-type 会自动拉起 niri,xdg-desktop-portal 配置也不生效

现象一:niri 会随登录自动拉起

home-environment 里用 (service home-niri-service-type),niri 并没有变成「登录界面里可选的一个会话」,而是成了个用户 shepherd 服务,用户一登录就被自动拉起来。

解决办法

不用这个服务,改为只把 niri 放进 home 的软件包列表:

(home-environment
 (packages (list niri ...)))

现象二:xdg-desktop-portal 配置不生效

niri 包自带的配置在 ~/.guix-home/profile/share/xdg-desktop-portal/niri-portals.conf,似乎没生效,OBS 等软件不知道怎么捕获屏幕:

❯ cat .guix-home/profile/share/xdg-desktop-portal/niri-portals.conf
[preferred]
default=gnome;gtk;
org.freedesktop.impl.portal.Access=gtk;
org.freedesktop.impl.portal.Notification=gtk;
org.freedesktop.impl.portal.Secret=gnome-keyring;

解决办法

home 配置里加上一份自己的:

(simple-service 'xdg-desktop-portal-config
                home-xdg-configuration-files-service-type
                `(("xdg-desktop-portal/niri-portals.conf" ,(plain-file
                                                            "niri-portals.conf"
                                                            "[preferred]
default=gnome;gtk;
org.freedesktop.impl.portal.Access=gtk;
org.freedesktop.impl.portal.Notification=gtk;
org.freedesktop.impl.portal.Secret=gnome-keyring;
org.freedesktop.impl.portal.FileChooser=gtk;
org.freedesktop.impl.portal.ScreenCast=gnome;
org.freedesktop.impl.portal.Screenshot=gnome;
"))))

FileChooser=gtk 是因为我用 pcmanfm-qt 而不是 nautilus。

7. 自组装桌面缺少 x11-socket-directory-service-type,Xwayland-satellite 二次启动失败

现象

niri 会话 + Xwayland-satellite:

  • 首次开机登录 niri,Xwayland 正常。
  • 注销(不重启)后再次登录,Xwayland-satellite 启动失败,报 /tmp/.X11-unix 权限不对。

原因

没有用 Guix 通用的 desktop 服务模块,而是自己拼各种模块,漏掉了 x11-socket-directory-service-type——它才负责创建并摆正 /tmp/.X11-unix 的权限。

  • 首次登录时该目录不存在,由 Xwayland-satellite 自行创建;在 umask 022 下权限被削成 755,此时还能凑合用。
  • 注销再登录时目录已存在但权限不是它要求的 1777,校验不通过,直接报错退出。

解决办法

导入模块并加入服务:

(use-service-modules desktop)
;; …
(service x11-socket-directory-service-type)

该服务是一个 one-shot shepherd 服务(依赖 file-systems),启动时执行 mkdir -p /tmp/.X11-unixchmod 1777,权限在首次登录前就已就位,之后反复登录不再出错。

8. Qt 图标不显示

现象

启动器的应用图标、文件夹图标等都不显示。

原因

那些是 svg 图标,需要 qtsvg 才能渲染。

解决办法

packages 列表里加上 qtsvg

(packages (list qtsvg
                ...))

9. pcmanfm-qt:打开方式里应用列表为空、没有垃圾桶

现象

  1. 右键 → 打开方式 → 其他应用程序,里面的「已安装的应用程序」列表是空的。
  2. 没有垃圾桶。

解决办法

packages 列表里补上 lxqt-menu-data(list glib "bin")

(packages (list pcmanfm-qt
                lxqt-menu-data        ; 打开方式里的应用列表
                (list glib "bin")     ; gio,垃圾桶要它
                ...))

10. virt-manager 默认 NAT 下虚拟机没网,改为自己声明的网桥

现象

用 virt-manager 默认的 NAT 网络,虚拟机就是没网。

原因

不确定。怀疑是 virt-manager 的 NAT 插入的路由规则在 Guix 上和 docker0 等起了冲突。

解决办法

别用默认 NAT,自己声明一个网桥(我这里是 vmnat0,网段 192.168.240.0/24),虚拟机改用这个网桥。

配置要点都在 system.scm 里:

  1. 建桥并给宿主机地址:
(service static-networking-service-type
         (list (static-networking (provision '(vmnat-networking))
                                  (links (list (network-link (name "vmnat0")
                                                             (type 'bridge)
                                                             (arguments '()))))
                                  (addresses (list (network-address
                                                    (device "vmnat0")
                                                    (value "192.168.240.1/24")))))))
  1. 在这个桥上发 DHCP / DNS:
(service dnsmasq-service-type
         (dnsmasq-configuration (shepherd-provision '(dnsmasq-vmnat0))
                                (shepherd-requirement '(vmnat-networking))
                                (extra-options (list
                                                "--except-interface=lo"
                                                "--interface=vmnat0"
                                                "--bind-dynamic"
                                                "--dhcp-range=192.168.240.100,192.168.240.254,255.255.255.0,12h"
                                                "--dhcp-option=option:router,192.168.240.1"
                                                "--dhcp-option=option:dns-server,192.168.240.1"))))
  1. 打开 IPv4 转发,并装 NAT 规则(vmnat0-firewall-service:INPUT 放行 vmnat0 的 DHCP/DNS,FORWARD 放行 vmnat0,POSTROUTING 对 192.168.240.0/24 做 MASQUERADE):
(modify-services %base-services
  (sysctl-service-type config =>
                       (sysctl-configuration (settings (cons '("net.ipv4.ip_forward" . "1")
                                                        %default-sysctl-settings)))))
  1. 让 dhcpcd 别管这个桥:
(service dhcpcd-service-type
         (dhcpcd-configuration (extra-content "denyinterfaces vmnat0\n")))
  1. 最后把虚拟机的网卡接到 vmnat0