Chapter 05

NixOS 系统配置基础

将整个操作系统的状态声明在一个文件里——可重建、可回滚、可版本控制

NixOS 的核心思想

NixOS 是将 Nix 哲学应用到整个操作系统的结果。传统 Linux 配置散落在 /etc 的数百个文件中,系统状态是通过执行一系列命令逐步累积形成的——难以复现,难以追踪。

NixOS 将系统配置集中到 /etc/nixos/configuration.nix 一个文件中(可以模块化拆分)。nixos-rebuild switch 原子化地将系统切换到配置所描述的精确状态,任何失败都可以立即回滚。

NixOS 工作原理 ──────────────────────────────────────────────────── /etc/nixos/configuration.nix │ nixos-rebuild switch ▼ Nix 评估配置 → 构建所有需要的包 → 生成新 generation │ ▼ 原子切换(符号链接更新) ├── /run/current-system → /nix/store/new-generation/ ├── /etc/ 配置文件更新 └── systemd 服务状态同步 │ ▼ (如果出错) nixos-rebuild switch --rollback └── 回到上一 generation(几秒内完成)

NixOS 安装概览

# 1. 从 nixos.org 下载 ISO 镜像,制作启动盘后引导

# 2. 分区(UEFI 示例)
parted /dev/sda -- mklabel gpt
parted /dev/sda -- mkpart ESP fat32 1MB 512MB
parted /dev/sda -- set 1 esp on
parted /dev/sda -- mkpart primary ext4 512MB 100%

# 3. 格式化
mkfs.fat -F 32 -n boot /dev/sda1
mkfs.ext4 -L nixos /dev/sda2

# 4. 挂载
mount /dev/disk/by-label/nixos /mnt
mkdir -p /mnt/boot
mount /dev/disk/by-label/boot /mnt/boot

# 5. 生成初始配置
nixos-generate-config --root /mnt
# 生成 /mnt/etc/nixos/configuration.nix 和 hardware-configuration.nix

# 6. 编辑配置(见下文),然后安装
nixos-install

# 7. 设置 root 密码,重启
reboot

configuration.nix 结构详解

# /etc/nixos/configuration.nix
# 这是一个接受 { config, pkgs, ... } 参数的函数
{ config, pkgs, ... }:

{
  # ── 基础设置 ──────────────────────────────
  system.stateVersion = "24.05";  # 不要随意修改!

  # 主机名
  networking.hostName = "my-nixos";

  # 时区
  time.timeZone = "Asia/Shanghai";

  # 语言
  i18n.defaultLocale = "zh_CN.UTF-8";

  # ── 引导器 ────────────────────────────────
  # systemd-boot(UEFI,推荐)
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  # 或者使用 GRUB(传统 BIOS)
  # boot.loader.grub.enable = true;
  # boot.loader.grub.device = "/dev/sda";

  # ── 网络 ──────────────────────────────────
  networking.networkmanager.enable = true;  # 桌面推荐
  # 或:networking.useDHCP = true;            # 服务器推荐

  # ── 系统全局软件包 ────────────────────────
  environment.systemPackages = with pkgs; [
    vim
    git
    curl
    wget
    htop
    tmux
    ripgrep
    fd
  ];

  # ── 用户配置 ──────────────────────────────
  users.users.alice = {
    isNormalUser = true;
    description = "Alice";
    extraGroups = [ "wheel" "networkmanager" "docker" ];
    shell = pkgs.zsh;
    # 初始密码(用 mkpasswd 生成哈希)
    hashedPassword = "$6$...";
    # 或:直接使用明文(不安全,仅用于初始设置)
    # initialPassword = "changeme";
  };

  # 允许 wheel 组 sudo
  security.sudo.enable = true;

  # ── 常用服务 ──────────────────────────────
  services.openssh = {
    enable = true;
    settings = {
      PermitRootLogin = "no";
      PasswordAuthentication = false;  # 只允许密钥认证
    };
  };

  # ── Nix 设置 ──────────────────────────────
  nix.settings.experimental-features = [ "nix-command" "flakes" ];

  # ── 防火墙 ────────────────────────────────
  networking.firewall.enable = true;
  networking.firewall.allowedTCPPorts = [ 22 80 443 ];
}

应用配置:nixos-rebuild

# 切换到新配置(立即生效,更新引导项)
sudo nixos-rebuild switch

# 切换但不更新引导默认项(仍可从引导菜单选择旧版本)
sudo nixos-rebuild test

# 只构建,不切换(验证配置语法和依赖)
sudo nixos-rebuild build

# 构建并在 VM 中测试(不影响真实系统)
sudo nixos-rebuild build-vm
./result/bin/run-*-vm   # 启动测试 VM

# 回滚到上一个 generation
sudo nixos-rebuild switch --rollback

# 查看所有 generation
sudo nix-env --list-generations --profile /nix/var/nix/profiles/system
# 输出示例:
#    1   2024-01-01 12:00:00
#    2   2024-01-15 09:30:00
#    3   2024-02-01 14:22:00  (current)

# 切换到特定 generation
sudo nix-env --switch-generation 2 --profile /nix/var/nix/profiles/system
sudo /nix/var/nix/profiles/system-2-link/bin/switch-to-configuration switch

从零配置一台开发机

以下是一个完整的开发机 configuration.nix 示例:

{ config, pkgs, ... }:

{
  system.stateVersion = "24.05";
  networking.hostName = "dev-machine";
  time.timeZone = "Asia/Shanghai";

  # UEFI 引导
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  # 网络
  networking.networkmanager.enable = true;

  # 开发工具
  environment.systemPackages = with pkgs; [
    # 终端工具
    vim neovim git curl wget htop tmux zsh fzf
    ripgrep fd bat eza delta
    # 开发语言
    python311 nodejs_20 go rustup
    # 容器
    docker docker-compose
    # 实用工具
    jq yq httpie direnv nix-direnv
  ];

  # 服务
  services.openssh.enable = true;
  virtualisation.docker.enable = true;

  # Zsh 为系统默认 Shell
  programs.zsh.enable = true;

  # 用户
  users.users.dev = {
    isNormalUser = true;
    extraGroups = [ "wheel" "docker" "networkmanager" ];
    shell = pkgs.zsh;
    openssh.authorizedKeys.keys = [
      "ssh-ed25519 AAAAC3... user@host"
    ];
  };

  # Nix 配置
  nix = {
    settings.experimental-features = [ "nix-command" "flakes" ];
    gc.automatic = true;
    gc.dates = "weekly";
    gc.options = "--delete-older-than 14d";
  };

  # 防火墙
  networking.firewall.allowedTCPPorts = [ 22 ];
}
将配置纳入 Git 版本控制

强烈建议将 /etc/nixos/ 目录(或其中的配置文件)纳入 Git 仓库:

cd /etc/nixos && git init && git add . && git commit -m "Initial NixOS config"

这样每次配置变更都有记录,可以随时查看差异(git diff)、回溯历史配置(git log),甚至在多台机器间同步配置。

stateVersion 不要随意修改

system.stateVersion 不是 NixOS 的版本,而是"系统状态初始化时的版本"。修改它可能导致某些服务配置升级脚本被跳过或重复执行。安装时生成什么值就保持什么值,即使升级 NixOS 版本也不需要修改。

本章小结

NixOS 的革命性在于将系统管理从"执行命令积累状态"变成"声明目标状态"。configuration.nix 是系统的唯一事实来源,nixos-rebuild switch 原子化实现目标状态,任何时候都可以回滚。下一章深入 NixOS 模块系统,学习如何配置更复杂的服务。