这是一篇持续更新的踩坑笔记,记录我在 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 reconfigure 和 guix 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 服务模块后:
- greetd 的 agetty 不管你登录 shell 配的是什么,登录进去只有 bash。
- 锁屏解不开。
原因
- agetty 那一层不读你在系统里配的登录 shell,所以只给 bash。
- 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-unix 并 chmod 1777,权限在首次登录前就已就位,之后反复登录不再出错。
8. Qt 图标不显示
现象
启动器的应用图标、文件夹图标等都不显示。
原因
那些是 svg 图标,需要 qtsvg 才能渲染。
解决办法
packages 列表里加上 qtsvg:
(packages (list qtsvg
...))
9. pcmanfm-qt:打开方式里应用列表为空、没有垃圾桶
现象
- 右键 → 打开方式 → 其他应用程序,里面的「已安装的应用程序」列表是空的。
- 没有垃圾桶。
解决办法
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 里:
- 建桥并给宿主机地址:
(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")))))))
- 在这个桥上发 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"))))
- 打开 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)))))
- 让 dhcpcd 别管这个桥:
(service dhcpcd-service-type
(dhcpcd-configuration (extra-content "denyinterfaces vmnat0\n")))
- 最后把虚拟机的网卡接到
vmnat0。