OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 086

NixOS on Raspberry Pi: images, hardware, and services

Move from SD images and first boot to board interfaces, common services, and maintenance on low-memory devices.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSNixOS node deployment and ARM devices ↗
About 5 min readStart reading

Based on nix.dev's Raspberry Pi tutorial, with additional material on hardware, services, and maintenance; this is not a word-for-word translation of the official tutorial: Installing NixOS on a Raspberry Pi

Run NixOS on a Raspberry Pi to build a reproducible ARM device.

Supported Raspberry Pi Models#

Model Scope Cautions
Raspberry Pi 4B The main target of this article's basic installation path Verify booting with the specific image and firmware
Raspberry Pi 3/3B+ The official tutorial says it can be adapted for use Do not directly reuse Pi 4 kernel and hardware options
Raspberry Pi Zero 2 W Check the image and hardware module separately Check memory constraints and device support
Raspberry Pi 5 Requires a separate model-specific support path “Install the latest kernel” is not a complete solution

This article retains examples based on 24.05; they do not represent the current release or promise compatibility with every model. The nix.dev reference tutorial still targets the Pi 4B. When choosing a nixos-hardware module, pin its revision and read the README for the specific model.

Prepare the SD Card#

1. Download a Prebuilt Image

# 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. Write It to the SD Card

The following dd command overwrites everything on the target device. Back up the SD card first, use lsblk to verify its capacity, model, and mount points, and unmount its partitions. Confirm that /dev/sdX is the entire SD card rather than the system disk. Do not execute the command with the placeholder path unchanged.

# 查找 SD 卡设备
lsblk

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

# 同步
sync

First-Boot Configuration#

1. Basic Configuration

The following is a historical Pi 4 configuration skeleton. The root filesystem, boot partition, and kernel are determined by the verified image and hardware module; initially retain the generated hardware-configuration.nix. Do not treat the SD image generation module as hardware configuration that every installed system should import:

{ 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. Configure WiFi

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

Use a runtime environment file readable only by root; do not copy secrets into the store through Nix path values:

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

Hardware Feature Support#

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 选项
}

Camera

The original notes use the older Raspberry Pi firmware camera path. Modern camera stacks and options vary with the board, firmware, and nixos-hardware module. The following preserves the historical meaning of the firmware configuration and cannot be used directly as a current NixOS module:

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

Common Service Configuration#

Home Automation (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";
      };
    };
  };
}

Media Center (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 Filtering)

Listening on all interfaces and opening the firewall expands the reach of the DNS service. Restrict it to the intended LAN interface during deployment and check for port 53 conflicts.

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

Performance Optimization#

Memory Optimization

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

Storage Optimization

fstrim works only if the storage device and the entire block-device chain support discard; not all SD cards do. tmpfs consumes RAM and potentially swap, so configure it within the memory budget of a low-memory device.

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

Improving SD Card Longevity#

Reducing continuous writes can slow wear. volatile logs are lost on reboot, so external logging is needed to troubleshoot problems across reboots:

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

Troubleshooting#

Unable to Boot

Check config.txt:

cat /boot/firmware/config.txt

WiFi Does Not Work

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

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

Excessive Temperature

First check cooling, power, and load. Adjusting the CPU governor is a performance/power policy; it does not add cooling or guarantee a direct limit on the maximum frequency:

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

First Activation and Recovery

Once the image boots, build your own system configuration first, then schedule nixos-rebuild boot and a reboot. Retain a known-bootable generation and a recovery route through a serial or local console. Unloading network drivers interrupts connectivity; do not do it blindly in a remote session. The placeholder SSH public-key file must actually exist. These configurations have not been tested on a physical Raspberry Pi.

References & further reading

  1. nix.dev Pi 4B installation tutorial ↗
  2. nixos-hardware model modules ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

NixOS node deployment and ARM devices

  1. 01Colmena: building, transferring, and activating multiple nodes
  2. 02NixOS on Raspberry Pi: images, hardware, and services
CONTINUE READING / NEXT ARTICLENixOS configuration organization, maintenance, and troubleshooting

Retain a complete working checklist for version control, module organization, update backups, secure development, and failure recovery.