OFFSET偏移
← 文章目录OFFSET / FIELD NOTESNO. 086

树莓派上的 NixOS:镜像、硬件与服务

从 SD 镜像和首次启动延伸到板卡接口、常用服务以及小内存设备维护。

所在专题NixOS 节点部署与 ARM 设备 ↗
约 12 分钟阅读开始阅读

以 nix.dev 的树莓派教程为起点整理,另有硬件、服务与维护扩展;不是官方教程的逐字翻译: Installing NixOS on a Raspberry Pi

在树莓派上运行 NixOS,打造可复现的 ARM 设备。

支持的树莓派型号#

型号 使用范围 注意事项
Raspberry Pi 4B 本篇基础安装路径的主要对象 以具体镜像和固件验证启动
Raspberry Pi 3/3B+ 官方教程说明可以调整后使用 不应直接套用 Pi 4 的内核和硬件选项
Raspberry Pi Zero 2 W 需要单独核对镜像与硬件模块 内存约束和设备支持需检查
Raspberry Pi 5 需要单独的型号支持路径 不能把“安装最新内核”当作完整方案

本文保留以 24.05 为背景的示例;它不是当前版本或所有型号的兼容承诺。nix.dev 的参考教程仍以 Pi 4B 为对象。选择 nixos-hardware 模块时固定其修订,并查看对应型号的 README。

准备 SD 卡#

1. 下载预构建镜像

# Raspberry Pi 4 (64位)
nix-shell -p wget zstd
# 从对应 Hydra 任务的成功构建复制实际 build-product URL
IMAGE_URL='https://hydra.nixos.org/build/<build-id>/download/1/<image>.img.zst'
wget -O nixos-sd-image-aarch64-linux.img.zst "$IMAGE_URL"

# 解压
unzstd nixos-sd-image-aarch64-linux.img.zst

2. 写入 SD 卡

以下 dd 会覆盖目标设备全部内容。先备份 SD 卡,使用 lsblk 核对容量、型号和挂载点,卸载其分区;确认 /dev/sdX 是整张 SD 卡而不是系统盘。占位路径不能直接执行。

# 查找 SD 卡设备
lsblk

# 写入(替换 /dev/sdX)
sudo dd if=nixos-sd-image-aarch64-linux.img of=/dev/sdX bs=4M conv=fsync status=progress

# 同步
sync

首次启动配置#

1. 基础配置

以下是 Pi 4 历史配置骨架;根文件系统、引导分区和内核由已验证镜像及硬件模块决定,先保留生成的 hardware-configuration.nix。不要把 SD 镜像生成模块当成所有已安装系统都应导入的硬件配置:

{ config, pkgs, lib, ... }:

{
  # 导入硬件配置
  imports = [
    ./hardware-configuration.nix
  ];
  
  # 启动配置
  boot = {
    kernelPackages = pkgs.linuxKernel.packages.linux_rpi4;
    loader.grub.enable = false;
    loader.generic-extlinux-compatible.enable = true;
  };
  
  # 固件
  hardware.enableRedistributableFirmware = true;
  hardware.firmware = [ pkgs.wireless-regdb ];
  
  # 网络
  networking = {
    hostName = "workstation";
    wireless.enable = true;
    interfaces.wlan0.useDHCP = true;
  };
  
  # SSH
  services.openssh = {
    enable = true;
    settings = {
      PermitRootLogin = "no";
      PasswordAuthentication = false;
    };
  };
  
  # 用户
  users.users.user = {
    isNormalUser = true;
    extraGroups = [ "wheel" "video" "audio" ];
    openssh.authorizedKeys.keyFiles = [ ./keys/user.pub ];
    # 本地登录密码通过运行时密码文件或首次控制台设置处理
  };
  
  # 系统版本
  system.stateVersion = "24.05";
}

2. 配置 WiFi

# 仅展示字段形状;不要把真实 WiFi 密码写入 Nix/store
# networking.wireless.networks."ExampleSSID".psk = "<wifi-password>";

使用仅 root 可读的运行时环境文件;密钥不要通过 Nix 路径值复制到 store:

{
networking.wireless.environmentFile = "/var/secrets/wireless.env";
networking.wireless.networks = {
  "ExampleSSID".psk = "@PASS@";
};
}

硬件特性支持#

GPIO

{ pkgs, ... }:

{
  # 真实 GPIO 依赖对应型号的内核与 device tree;gpio-mockup 仅模拟 GPIO
  # boot.kernelModules = [ "gpio-mockup" ];
  
  # 安装工具
  environment.systemPackages = with pkgs; [
    libgpiod
    raspberrypifw
  ];
  
  # 用户权限
  users.groups.gpio = {};
  users.users.user.extraGroups = [ "gpio" ];
  services.udev.extraRules = ''
    KERNEL=="gpiochip*", GROUP="gpio", MODE="0660"
  '';
}

I2C

{
  boot.kernelModules = [ "i2c-dev" ];
  hardware.i2c.enable = true;
  users.users.user.extraGroups = [ "i2c" ];
}

SPI

{
  boot.kernelModules = [ "spidev" ];
  # SPI controller 还须由对应板卡的 device-tree overlay 启用
  # hardware.spi.enable 不是通用 NixOS 选项
}

摄像头

原笔记使用旧 Raspberry Pi 固件相机路径。现代相机栈和选项随板型、固件与 nixos-hardware 模块变化;以下保留固件配置的历史语义,不能直接当作当前 NixOS 模块:

{
  boot.loader.raspberryPi.firmwareConfig = ''
    start_x=1
    gpu_mem=256
  '';
  
  # hardware.raspberry-pi.camera.enable 并非所有 NixOS 都有的选项
  # 应先核对目标型号模块,再启用对应相机栈
}

常用服务配置#

家庭自动化 (Home Assistant)

{
  services.home-assistant = {
    enable = true;
    openFirewall = true;
    config = {
      homeassistant = {
        name = "Home";
        latitude = 0.0;   # 示例坐标,部署时使用所需位置
        longitude = 0.0;
        elevation = 0;
        unit_system = "metric";
        time_zone = "Asia/Shanghai";
      };
    };
  };
}

媒体中心 (Kodi)

{
  services.xserver.desktopManager.kodi = {
    enable = true;
  };
  
  services.displayManager.autoLogin = {
    enable = true;
    user = "kodi";
  };
  
  users.users.kodi = {
    isNormalUser = true;
    extraGroups = [ "audio" "video" ];
  };
}

AdGuard Home(DNS 过滤)

监听所有接口并开防火墙会扩大 DNS 服务范围;部署时限制到目标局域网接口,并核对 53 端口冲突。

{
  services.adguardhome = {
    enable = true;
    openFirewall = true;
    settings = {
      dns = {
        bind_hosts = [ "0.0.0.0" ];
        port = 53;
      };
    };
  };
}

性能优化#

内存优化

{
  # ZRAM 交换
  zramSwap.enable = true;
  zramSwap.memoryPercent = 50;
  
  # 减少日志保留
  services.journald.extraConfig = ''
    SystemMaxUse=100M
    MaxFileSec=7day
  '';
  
  # 禁用不需要的服务
  documentation.nixos.enable = false;
  services.xserver.enable = false;  # 无图形界面
}

存储优化

fstrim 仅在存储设备和整条块设备链支持 discard 时有效;并非所有 SD 卡都支持。tmpfs 会占用内存和可能的交换空间,需要与小内存设备预算一起设置。

{
  # 启用 discard 支持
  services.fstrim.enable = true;
  
  # 临时文件使用 tmpfs
  boot.tmp.useTmpfs = true;
  boot.tmp.tmpfsSize = "50%";
}

SD 卡持久性优化#

减少持续写入可延缓磨损;volatile 日志重启后丢失,需要外部日志才能排查跨重启问题:

{
  # 限制日志写入
  services.journald.storage = "volatile";
  
  # RAM 中的 /tmp
  boot.tmp.useTmpfs = true;
  
  # 禁用不必要的服务
  systemd.services.systemd-journal-flush.enable = false;
}

排查问题#

无法启动

检查 config.txt:

cat /boot/firmware/config.txt

WiFi 不工作

# 检查固件
sudo dmesg | grep -i wifi

# 重新加载驱动
sudo modprobe -r brcmfmac
sudo modprobe brcmfmac

温度过高

先检查散热、电源和负载。CPU governor 调整是性能/功耗策略,不等于加装散热,也不保证直接限制最高频率:

{
  # 限制 CPU 频率
  powerManagement.cpuFreqGovernor = "conservative";
}

初次应用与恢复

镜像能启动后,先构建自己的系统配置,再安排 nixos-rebuild boot 和重启。保留已知可启动世代、串口或本地控制台恢复路径。网络驱动卸载会中断连接,远程会话不要盲目执行。SSH 公钥占位文件需实际存在;这些配置未在实体树莓派上运行测试。

参考与延伸

  1. nix.dev Pi 4B 安装教程 ↗
  2. nixos-hardware 型号模块 ↗

根据技术笔记重新整理,示例使用通用名称。

沿专题继续阅读

NixOS 节点部署与 ARM 设备

  1. 01Colmena:多节点构建、传输与激活
  2. 02树莓派上的 NixOS:镜像、硬件与服务
CONTINUE READING / 下一篇NixOS 配置组织、维护与排错实践

保留版本管理、模块组织、更新备份、安全开发和故障恢复的完整工作清单。