跳至主要內容

NixOS 配置

NixOS-CN大约 14 分钟

NixOS 配置

内容施工中

本节内容正在修缮中,当前内容可能已经过时,仅供参考。

包管理

这一节我们会讨论如何为你的系统添加额外的包。NixOS 包管理的方式有两种:

  • 配置文件声明。你可以在配置文件为系统或用户声明需要安装的包,每次你重新生成系统,NixOS 都会确保本地包含了你指定的这些包。这是持久的。

  • 非持久环境。使用 nix-env 管理软件包安装,升级与卸载。这种方式允许不同软件仓库频道版本的包共存。这也是非 root 用户唯一安装软件包的方式。

声明式包管理

configuration.nix 中, 提供用于声明系统环境包含的包的 environment.systemPackages 数组:

environment.systemPackages = [ pkgs.firefox ];  # 将来源于 pkgs(Nixpkgs) 的包安装到系统

配置文件并不是实时生效的 。你需要运行 sudo nixos-rebuild switch 来生成当前配置文件描述的系统。

依赖配置

对于某些包(例如依赖 D-Bus 或 systemd 服务注册的包),仅仅是安装还是不够的,我们需要为它们对系统进行一些配置。你可以访问选项列表open in new window来检索需要启用的 NixOS 模块。

你可以使用以下命令获取在线包仓库中可用的软件包的列表:

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

通常会输出很多行可以获取的包的信息。第一列输出是属性名(例如nixos.firefox)。

前缀

nixos 前缀表明当前包是从 nixos 频道获取的。

如果你想卸载这个包,修改配置后重新生成系统即可。

定制软件包

一些软件包会提供一些禁用或启用功能,更改一些特性的选项。例如 Firefox 插件捆绑包(会额外提供一些诸如 Adobe Flash Player 的插件)会提供一个启用 Google Talk 的选项,如此配置便可以:

nixpkgs.config.firefox.enableGoogleTalkPlugin = true;

难以查询

遗憾的是,Nixpkgs 依然无法提供一种简易查询这些选项的方式。

除了高阶选项外,你还可以以几乎任意方式调整软件包,例如更改或禁用软件包的依赖项。例如,Nixpkgs 中的 Emacs 软件包默认依赖于 GTK 2。如果你想将其构建为使用 GTK 3 的软件包,可以按如下方式指定:

environment.systemPackages = [ (pkgs.emacs.override { gtk = pkgs.gtk3; }) ];

我们使用了 override 函数指定了用户定义的参数,意味着我们覆写了一部分原本的参数,构建的包也因此改变了。让我们来细看这个语句,gtk 默认是接受 pkgs.gtk2 参数的,我们使用 pkgs.gtk3 作输入的时候,默认参数已经失效了,于是构建出来的包是依赖 GTK 3 的了。

运算优先级

圆括号是必要的,因为在 Nix 语言中,列表构造优先级高于函数。如果不使用括号,列表将会认为它接收了两个元素。

我们还可以使用 overrideAttrs 函数做出更多定制。override 函数的机制是覆写了包输入函数的参数,而 overrideAttrs 允许覆写传递给 mkDerivation 的属性。如此你几乎能修改这个包的方方面面,甚至包括源码。如果你想覆写源码输入,可以这样做:

environment.systemPackages = [
  (pkgs.emacs.overrideAttrs (oldAttrs: {
    name = "emacs-25.0-pre";
    src = /path/to/my/emacs/tree;  # 你的源码目录
  }))
];

在这里,pkgs.emacs 被衍生出了一个重新调用 stdenv.mkDerivation 并替换了 namesrc 属性的版本。然后 overrideAttrs 接收了这个衍生,成为了目前系统环境的 emacs 包。

添加自定义包

使用 Nix 语言构建

使用预构建文件

大多数可执行文件都不能在 NixOS 上直接工作,但是将依赖一起打包的容器格式就可以,常见的容器格式有 flatpaksAppImages 等。

非持久包管理(Ad-Hoc 包管理)

这种方式不持久是相对于声明式包管理的。声明式包管理可以保证系统的一致性和可复制性,所以是持久的。不过 Ad-Hoc 方式非常灵活。

Ad-Hoc

这个词语的起源是拉丁语,意思是“为此”。它通常表示一种针对特定目的,问题或任务的解决方案,而不是一种可以适用于其他情况的通用解决方案。例如,一个政府为了解决一个具体问题而设立的委员会就是一个 ad-hoc 委员会。更宽松地说,它也可以意味着“自发的 ”,“未计划的”或“即兴的”。

在 nixos 中,Ad-Hoc 的意义是指可以在一个临时的 shell 环境中使用任何用 nix 打包的程序,而不需要永久地安装它。这样可以方便地创建和使用开发环境,而不影响系统的状态。

通过 nix-env 命令,你可以像常规发行版那样使用命令安装软件:

nix-env -iA nixos.thunderbird

如果以 root 权限执行此语句,软件包将会被安装到 /nix/var/nix/profiles/default,并且对所有用户可见。如果你是普通用户,软件包将会安装到/nix/var/nix/profiles/per-user/username/profile,并且仅对当前用户可见。-A 参数指定了软件包所属的属性,如果不带属性,直接匹配 thunderbird 速度会较慢,同时也可能匹配到多个名称相同的包,产生歧义。

假设我们需要更新的包来自于系统频道,我们可以先更新系统频道,然后重新安装或更新指定包:

nix-channel --update nixos  # 更新系统频道
nix-env -iA nixos.thunderbird  # 再次安装时,此包会被替换为最新版

上面的语句可以指定另外的频道,从而用不同频道的包去代替当前包。如果你想用当前包默认的来源升级包,尝试这样做:

nix-channel --update nixos
nix-env -u thunderbird

如果你想升级所有包,可以这样:

nix-env -u '*'

但是需要注意, 它并不会升级系统配置中描述的包 ,那些包由 nixos-rebuild switch 命令管理。

如果你想更新那些包, nixos-rebuild switch --upgrade 即可,它会自动更新频道并更新系统生成。

如果你想卸载使用命令安装的包,使用以下命令:

nix-env -e thunderbird

此外,用户环境的状态是可以回滚的:

nix-env --rollback

你可以查阅 nix-env 手册页open in new window获取更多信息。

用户管理

类似的,NixOS 支持声明式用户管理和命令行用户管理。

声明式用户管理

我们以下面的例子说明声明式用户管理的大致细节:

users.users.alice = {
  isNormalUser = true;
  home = "/home/alice";
  description = "Alice Foobar";
  extraGroups = [ "wheel" "networkmanager" ];
  openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ];
};

根据描述,该用户加入了 wheel 组,意味着它可以使用 sudo 命令提权,此外它还加入了 networkmanager 组,意味着该用户可以配置网络。不过这样创建出来的用户是没有初始密码的,你仍需要使用 passwd 命令为其分配密码, 每次重新生成系统的时候不会影响到密码的状态 。对于 ssh 连接,你可以指定认证密钥,只要公钥与私钥匹配就能连接。

如果你设置 users.mutableUsersFalse/etc/passwd/etc/group 目录的内容将与配置文件中描述的一致。例如,如果你从配置文件中删除了某位用户,然后重新生成系统,这个用户就真实消失了。同时通过命令行管理用户的方式将失效。不过你仍然可以通过设置用户的hashedPasswordopen in new window 选项来分配密码。

用户 uid 是自动分配的,不过你也可以自行指定:

uid = 1000;

gid 分配也是自动的,同样可以用户定义,也是类似的方法:

users.groups.students.gid = 1000;

命令行式用户管理

创建一个名为 alice 的用户,-m 参数用于给该用户创建 home 目录:

useradd -m alice

为了让 nix 工具集可以为该用户所用,我们还需要给这个用户打开 login shell(加载用户配置的 shell)。这一步会把 ~/.nix-defexpr 链接到该用户的目录,这样该用户才能使用 nix 的一系列命令。

su - alice -c "true"

我们还需要为其分配密码,才能登录:

passwd alice
Enter new UNIX password: ***
Retype new UNIX password: ***

可以使用 userdel -r alice 删除该用户,-r 参数用于移除该用户的 home 目录。此外还有 usermodgroupadd, groupmodgroupdel 可以使用。

文件系统

你可以使用 fileSystems 来配置文件系统,然后按照挂载点配置文件系统,分区的参数等等:

fileSystems."/data" =
  { device = "/dev/disk/by-label/data";
    fsType = "ext4";
  };

这条配置生成 /etc/fstab,系统在开机时会根据这个表文件来挂载分区。

device 不一定要根据 label 来指定,也可以通过 uuid

块的 UUID

你可以用下面的方法查看到这些块的 UUID:

tritium@KOVA ~> lsblk -o name,mountpoint,size,uuid
NAME
    MOUNTPOINT         SIZE UUID
sda                  363.3M
sdb [SWAP]               2G 1159b63e-3072-4483-b374-78cd487e6460
sdc                      1T 8108c250-d488-4724-9237-5d926569fbef
sdd /mnt/wslg/distro     1T 8677e11d-56ab-4ecb-8dfd-8effb322493f

在默认情况下,所有被写在配置的分区都会被自动挂载,除非你指定了 noauto 的选项:

你也可以缺省 fsType 的值,因为它会自动检测文件系统类型。

nofail

如果 fstab 内容有误,系统会在启动时显示令人窒息的急救 Shell。为了避免这种情况,你可以在 option 里加入 nofail 来确保挂载是异步的且不会严重影响启动。

显示系统

X 窗口系统已经是上个世纪八十年代的软件了,冗余的功能拖累了它的性能,因此建议没有特殊需求的用户勇敢试水 Wayland。

X11

X 窗口系统可以提供最基础,成熟(稍微过时)的显示服务,只需要以下配置就能启用:

services.xserver.enable = true;

它会自动检测并启用适合的 xorg 驱动,比如 mesaxf86 系列驱动。当然你也可以手动指定:

services.xserver.videoDrivers = [ "r128" ];

通过以上配置就启用了 xf86-video-r128 驱动。

然后你应该至少启用一个桌面管理器或窗口管理器。我通常为新手和不喜欢折腾的人推荐桌面管理器,窗口管理器为装逼和喜欢效率的极客所用。

#  挑一个喜欢的吧
services.xserver.desktopManager.plasma5.enable = true;
services.xserver.desktopManager.xfce.enable = true;
services.xserver.desktopManager.gnome.enable = true;
services.xserver.desktopManager.mate.enable = true;
services.xserver.windowManager.xmonad.enable = true;
services.xserver.windowManager.twm.enable = true;
services.xserver.windowManager.icewm.enable = true;
services.xserver.windowManager.i3.enable = true;
services.xserver.windowManager.herbstluftwm.enable = true;

NixOS 的默认显示管理器是 LightDM(它只在 X11 下工作),你也可以自行选择替代品:

services.xserver.displayManager.sddm.enable = true;  # KDE 的默认登录管理器
services.xserver.displayManager.gdm.enable = true;  # GNOME 的默认登陆管理器

你还可以指定 x11 的键盘布局:

services.xserver.layout = "de";
services.xserver.xkbVariant = "neo";

并且 x11 显示服务可以被手动启用或重启:

systemctl restart --now display-manager.service

在 64 位系统上,如果你想运行 32 位的 OpenGL 程序(比如 Wine)。你应该这样配置:

hardware.opengl.driSupport32Bit = true;

自动登录

如果你的电脑不需要限制其他人访问,你可以设置自动登录来跳过恼人的用户登录界面。在下面的例子中,我们定义了默认的会话,以便于自动登录到它:

services.xserver.displayManager.defaultSession = "none+i3";

格式是“桌面环境”+“窗口管理器”,但是光配置默认会话还不够,还需要将开启自动登陆的布尔值传递给显示管理器,并指定自动登录的用户是谁:

services.xserver.displayManager.lightdm.enable = true;
services.xserver.displayManager.autoLogin.enable = true;
services.xserver.displayManager.autoLogin.user = "alice";

Intel 图形驱动

通常有两种驱动供 Intel 核心显卡用户选择:modesettingxf86-video-intel。按照正常人的脑子想,肯定是带有 intel 的专用驱动性能和稳定性更佳,但实际情况是,后者缺乏维护,过时且不稳定。

modesetting 是一种运行在 KMS(Kernel Mode Setting)的通用驱动:

services.xserver.videoDrivers = [ "modesetting" ];

如果你想启用定制驱动,也不是不可以:

services.xserver.videoDrivers = [ "intel" ];

这样你就启用了 xf86-video-intel 驱动。

如果你遇到屏幕撕裂的问题,尝试以下设置:

services.xserver.videoDrivers = [ "intel" ];
services.xserver.deviceSection = ''
  Option "DRI" "2"
  Option "TearFree" "true"
'';

如果上面的改动不生效,也有可能是 Intel 自刷新适应的锅,在内核启动参数里加上 i915.enable_psr=0 也许会修复这种屏幕只刷新一半的情况(通常出现在机械革命 F1,联想 Yoga 14s 等机型上)。

NVDIA 闭源驱动

皮衣客打死不做个能用的开源黄伟达驱动。由于专有驱动不自由,所以你需要手动启用它:

services.xserver.videoDrivers = [ "nvidia" ];

如果你的显卡很老了,那可能需要老驱动才能驱动它:

services.xserver.videoDrivers = [ "nvidiaLegacy390" ];
services.xserver.videoDrivers = [ "nvidiaLegacy340" ];
services.xserver.videoDrivers = [ "nvidiaLegacy304" ];

NVDIA 开源驱动

  • nvdia 不公开其显卡的硬件规格和编程接口,导致开源驱动开发者无法完全利用显卡的功能和性能。
  • nvdia 为其专有驱动添加了一些安全机制,如签名验证,固件加密等,使得开源驱动无法加载或修改这些驱动。

nouveau 就是那个自由和开源的驱动 NVIDA 显卡的程序。nouveau 的目标是利用逆向工程 Nvidia 的专有 Linux 驱动程序来创建一个开放源码的驱动程序。

services.xserver.videoDrivers = [ "nouveau" ];

“nouveau” 是法语中的 “新的” 的意思。

效果见仁见智吧,反正 NVDIA 显卡的 Linux 用户日常没人权。

AMD 闭源驱动

AMD 的闭源驱动经常会坏掉,不建议使用:

services.xserver.videoDrivers = [ "amdgpu-pro" ];

建议用下面的开源驱动。

AMD 开源驱动

services.xserver.enable = true;
services.xserver.videoDrivers = [ "amdgpu" ];

触控板

笔记本电脑通常会带有触控板,启用配置如下:

services.xserver.libinput.enable = true;

这个驱动还有很多可配置的功能,例如关闭“触碰以点击”功能:

services.xserver.libinput.touchpad.tapping = false;

废弃的 synaptics 选项

services.xserver.synaptics 选项在 NixOS 17.09 之后的版本中被废弃。

GTK/QT 主题

所有的主题你可以去仓库里找到。如果你想让 QT 和 GTK 的主题一致一些:

qt.enable = true;
qt.platformTheme = "gtk2";
qt.style = "gtk2";

自定义键盘布局(XKB)

这是一个例子,在这个例子中,我们稍微改造一下美式键盘。我们先创建一个文件us-greek,放到一个 symbols 文件夹下面,在里面描述自定义的键位:

xkb_symbols "us-greek"
{
  include "us(basic)"            // 包含基础美式键盘
  include "level3(ralt_switch)"  // 配置右 alt 作为一个第三级开关

  key <LatA> { [ a, A, Greek_alpha ] };
  key <LatB> { [ b, B, Greek_beta  ] };
  key <LatG> { [ g, G, Greek_gamma ] };
  key <LatD> { [ d, D, Greek_delta ] };
  key <LatZ> { [ z, Z, Greek_zeta  ] };
};

一个最小键盘布局还需要以下内容:

services.xserver.extraLayouts.us-greek = {
  description = "US layout with alt-gr greek";
  languages   = [ "eng" ];
  symbolsFile = /yourpath/symbols/us-greek;
};

extraLayouts. 后的名称应该与布局名匹配。

但是你要知道,你写的 XKB 布局要是坏的,X 显示系统会崩掉的,所以强烈建议先测试再实装:

nix-shell -p xorg.xkbcomp
setxkbmap -I/yourpath us-greek -print | xkbcomp -I/yourpath - $DISPLAY

你还可以从预置的 XKB 文件获取获取灵感:

echo "$(nix-build --no-out-link '<nixpkgs>' -A xorg.xkeyboardconfig)/etc/X11/xkb/"

更改配置以后,你需要注销再登录才能生效。 然后你键入 setxkbmap us-greek 并键入 Alt + a(可能在你的终端不会立马生效)。如果你要更改默认行为,还是配置一下services.xserver.layout

一个布局可以拥有除了 xkb_symbols 以外的数个其他组件,比如我们可以为一些键码绑定多媒体功能。我们可以通过使用 pkgs.xorg.xev 来找到中意的按键的码,然后再创建定义:

xkb_keycodes "media"
{
 <volUp>   = 123;
 <volDown> = 456;
}

现在我们来引入刚刚新定义的键码:

xkb_symbols "media"
{
 key.type = "ONE_LEVEL";
 key <volUp>   { [ XF86AudioLowerVolume ] };
 key <volDown> { [ XF86AudioRaiseVolume ] };
}

在这里完成总装:

services.xserver.extraLayouts.media = {
  description  = "Multimedia keys remapping";
  languages    = [ "eng" ];
  symbolsFile  = /path/to/media-key;
  keycodesFile = /path/to/media-sym;
};

上面的两个自定义目录用于引用上面的两个 nix 模块。

Wayland

X11 其实是一种显示协议,X.orgopen in new window 才是显示服务器。Wayland 作为新生的显示服务器,非常缺乏生态,只能用某些方式才能部分兼容 X11。尽管如此,还是希望所有人能加入 Wayland 的阵营。

X11 在设计之初就分为了服务端与窗口管理器,而一个 Wayland 混成器则类似 X 窗口管理器里内置了 服务端的功能,这样的设计让 Wayland 在本机体验上有了不少的提升。

Sway 是一个支持 Wayland 的窗口管理器,类似的软件还有 Wayfire,Hyprland 等:

programs.sway.enable = true;

如果你使用基于 wlroot 的窗口管理器,并且有共享屏幕或录制屏幕的需求,记得启用的 wlr 对应的 portal。

xdg.portal.wlr.enable = true;

与之相关的还有 services.pipewire.enable

GPU 加速

为了能够流畅的硬件解码视频,加速图形渲染等,我们需要配置一些常见的硬件加速 API。

桌面环境

XFCE

GNOME

KDE