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.zst2. 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
# 同步
syncFirst-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.txtWiFi Does Not Work
# 检查固件
sudo dmesg | grep -i wifi
# 重新加载驱动
sudo modprobe -r brcmfmac
sudo modprobe brcmfmacExcessive 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
Adapted from technical notes. Examples use generic names.