Difference between revisions of "NixOS on ARM"
(revision / format nixos flake update ARM) |
Tag: Undo |
||
Line 1: | Line 1: | ||
− | + | ## NixOS installation & configuration | |
− | + | The installation image contains a MBR partition table with two partitions: a FAT16/32 `/boot` partition and an ext4 root filesystem. This design allows you to directly reuse the SD image's partition layout and "install" NixOS on the same storage device by replacing the default configuration and running `nixos-rebuild`. On first boot, the image automatically resizes the rootfs partition to utilize all available storage space. | |
− | The installation image | ||
− | NixOS on ARM supports two | + | NixOS on ARM supports two approaches to system configuration: traditional configuration.nix files and the now-standard Flakes approach (recommended since NixOS 25.05). Both methods are covered below. |
− | Boot Process Overview | + | ### Boot Process Overview |
− | |||
− | U-Boot | + | All ARM boards in NixOS use U-Boot as the bootloader, alongside U-Boot's Generic Distro Configuration Concept to communicate boot information (kernel zImage path, initrd, DTB, command line arguments). U-Boot scans storage devices for `/extlinux/extlinux.conf` or `/boot/extlinux/extlinux.conf` files (generated by NixOS) and uses the bootable partition flag. |
− | + | U-Boot provides both an interactive shell and generation selection menu (similar to GRUB). Board-specific input/display support varies - check your device's wiki page for details. | |
− | |||
− | + | ### Basic Setup with configuration.nix | |
− | |||
− | |||
− | + | To generate a default `/etc/nixos/configuration.nix` file and detect hardware: | |
− | |||
− | |||
− | + | ```bash | |
+ | sudo nixos-generate-config | ||
+ | ``` | ||
+ | |||
+ | Here's a modern configuration template suitable for most ARM boards: | ||
+ | |||
+ | ```nix | ||
{ config, pkgs, lib, ... }: | { config, pkgs, lib, ... }: | ||
{ | { | ||
− | + | # NixOS wants to enable GRUB by default | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
boot.loader.grub.enable = false; | boot.loader.grub.enable = false; | ||
+ | |||
+ | # Enables the generation of /boot/extlinux/extlinux.conf | ||
boot.loader.generic-extlinux-compatible.enable = true; | boot.loader.generic-extlinux-compatible.enable = true; | ||
− | + | ||
− | # | + | # Import hardware configuration generated by nixos-generate-config |
− | # | + | # This should correctly set up your file systems |
+ | imports = [ ./hardware-configuration.nix ]; | ||
+ | |||
+ | # File systems configuration (if not correctly detected) | ||
+ | # Usually nixos-generate-config handles this properly, | ||
+ | # but this is provided as a fallback | ||
+ | /* | ||
+ | fileSystems = { | ||
+ | "/" = { | ||
+ | device = "/dev/disk/by-label/NIXOS_SD"; | ||
+ | fsType = "ext4"; | ||
+ | }; | ||
+ | }; | ||
+ | */ | ||
+ | |||
+ | # Adding a swap file is optional, but recommended for RAM-constrained devices | ||
+ | # Size is in MiB | ||
+ | # swapDevices = [ { device = "/swapfile"; size = 1024; } ]; | ||
+ | |||
+ | # Enable basic networking | ||
networking.networkmanager.enable = true; | networking.networkmanager.enable = true; | ||
− | + | ||
− | + | # Basic firewall | |
− | # Basic firewall | ||
networking.firewall.enable = true; | networking.firewall.enable = true; | ||
− | + | ||
# Set your time zone | # Set your time zone | ||
− | time.timeZone = "UTC"; | + | time.timeZone = "UTC"; |
− | + | ||
− | + | # System state version - IMPORTANT: Do not change this after initial install | |
− | + | system.stateVersion = "25.05"; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | # System state version - IMPORTANT: | ||
− | |||
− | system.stateVersion = "25.05" | ||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | ``` | |
− | + | Apply your configuration with: | |
+ | ```bash | ||
sudo nixos-rebuild switch | sudo nixos-rebuild switch | ||
− | + | ``` | |
− | |||
− | + | ### Kernel Selection | |
− | |||
− | + | Kernel selection depends on your specific ARM device: | |
− | |||
− | |||
− | + | - **For boards with good mainline support** (including Raspberry Pi 4/5 on aarch64): | |
− | For boards | + | ```nix |
− | + | boot.kernelPackages = pkgs.linuxPackages_latest; | |
− | + | ``` | |
− | + | - **For legacy Raspberry Pi models on armv6/armv7**: | |
+ | ```nix | ||
+ | boot.kernelPackages = pkgs.linuxPackages_rpi; | ||
+ | ``` | ||
− | + | - **For boards requiring specialized kernels**: | |
+ | Refer to the board-specific wiki page for recommended kernel packages. | ||
− | + | ### Modern Flakes-Based Approach (Recommended) | |
− | ( | ||
− | + | Since NixOS 25.05, Flakes are the standard recommended approach for system configuration. They provide reproducible builds, locked dependencies, and simplify cross-device management. | |
− | + | 1. Create a `flake.nix` file: | |
− | + | ```nix | |
{ | { | ||
description = "NixOS configuration for my ARM device"; | description = "NixOS configuration for my ARM device"; | ||
− | + | ||
inputs = { | inputs = { | ||
− | |||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; | nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; | ||
− | |||
− | |||
nixos-hardware.url = "github:NixOS/nixos-hardware"; | nixos-hardware.url = "github:NixOS/nixos-hardware"; | ||
− | |||
− | |||
− | |||
− | |||
}; | }; | ||
− | + | ||
− | outputs = { self, nixpkgs, nixos-hardware, ... } | + | outputs = { self, nixpkgs, nixos-hardware, ... }: { |
− | + | nixosConfigurations.mydevice = nixpkgs.lib.nixosSystem { | |
− | nixosConfigurations. | ||
system = "aarch64-linux"; # Or "armv6l-linux"/"armv7l-linux" for 32-bit ARM | system = "aarch64-linux"; # Or "armv6l-linux"/"armv7l-linux" for 32-bit ARM | ||
− | |||
modules = [ | modules = [ | ||
− | # Hardware-specific modules ( | + | # Hardware-specific modules (if available) |
# nixos-hardware.nixosModules.raspberry-pi-4 | # nixos-hardware.nixosModules.raspberry-pi-4 | ||
# nixos-hardware.nixosModules.raspberry-pi-5 | # nixos-hardware.nixosModules.raspberry-pi-5 | ||
− | + | ||
# Your main configuration file | # Your main configuration file | ||
./configuration.nix | ./configuration.nix | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
]; | ]; | ||
}; | }; | ||
}; | }; | ||
} | } | ||
− | + | ``` | |
− | + | 2. Apply your configuration with: | |
− | sudo nixos-rebuild switch --flake .# | + | ```bash |
− | + | sudo nixos-rebuild switch --flake .#mydevice | |
+ | ``` | ||
− | + | The `#mydevice` part corresponds to the `nixosConfigurations.mydevice` entry in your flake.nix file. | |
− | |||
− | The | ||
− | + | ### Binary Caches | |
− | # | + | #### AArch64 (ARM64) |
+ | The official NixOS Hydra instance builds full binary sets for the AArch64 architecture, available on https://cache.nixos.org for both the nixpkgs-unstable and stable channels: | ||
+ | |||
+ | ```nix | ||
nix.settings = { | nix.settings = { | ||
substituters = [ | substituters = [ | ||
"https://cache.nixos.org" | "https://cache.nixos.org" | ||
− | |||
]; | ]; | ||
trusted-public-keys = [ | trusted-public-keys = [ | ||
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" | "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" | ||
− | |||
]; | ]; | ||
}; | }; | ||
− | + | ``` | |
− | |||
− | Using | + | #### armv6l and armv7l (32-bit ARM) |
− | Setting up your own binary cache | + | There are currently no official binary caches for 32-bit ARM. Consider: |
− | + | - Building packages natively (slow on low-power devices) | |
+ | - Using cross-compilation (see below) | ||
+ | - Setting up your own binary cache | ||
− | + | For better performance on bandwidth-constrained devices, limit parallel connections: | |
− | + | ```nix | |
nix.settings = { | nix.settings = { | ||
− | + | max-substitution-jobs = 2; | |
− | max-substitution-jobs = | ||
}; | }; | ||
− | Building on Resource-Constrained ARM Devices | + | ``` |
− | + | ||
+ | ### Building on Resource-Constrained ARM Devices | ||
+ | |||
+ | ARM devices, especially older or low-power models, may struggle with building large packages. Consider these approaches: | ||
− | Remote Builders | + | #### 1. Remote Builders |
− | + | Configure a more powerful machine (ideally ARM64) as a remote builder: | |
− | + | ```nix | |
nix.buildMachines = [{ | nix.buildMachines = [{ | ||
− | hostName = " | + | hostName = "builder"; |
− | system = "aarch64-linux"; | + | system = "aarch64-linux"; |
− | maxJobs = | + | maxJobs = 4; |
− | speedFactor = | + | speedFactor = 2; |
supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ]; | supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ]; | ||
− | |||
− | |||
}]; | }]; | ||
nix.distributedBuilds = true; | nix.distributedBuilds = true; | ||
− | + | ``` | |
+ | |||
+ | For setup instructions, see the [Distributed Build](https://nixos.wiki/wiki/Distributed_build) wiki page. | ||
− | Cross-Compilation using QEMU | + | #### 2. Cross-Compilation using QEMU with binfmt_misc |
− | + | On an x86_64 NixOS system, enable ARM emulation: | |
− | + | ```nix | |
− | boot.binfmt.emulatedSystems = [ "aarch64 | + | boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; |
− | + | ``` | |
− | + | Then build ARM packages from your x86_64 machine using: | |
− | + | ```bash | |
− | nix build --system aarch64-linux .# | + | nix build --system aarch64-linux .#package |
+ | ``` | ||
− | + | Or for non-Flake commands: | |
+ | |||
+ | ```bash | ||
nix-build '<nixpkgs>' -A pkgs.hello --argstr system aarch64-linux | nix-build '<nixpkgs>' -A pkgs.hello --argstr system aarch64-linux | ||
− | + | ``` | |
− | + | #### 3. Building through QEMU/KVM | |
− | + | While slower than other methods, full emulation through QEMU/KVM is possible for ARM development. See the [NixOS on ARM/QEMU](https://nixos.wiki/wiki/NixOS_on_ARM/QEMU) page for detailed setup. | |
− | |||
− | + | ### Creating Custom ARM Images | |
− | |||
− | # | + | #### Building with Flakes (Recommended) |
+ | |||
+ | Create a custom SD card image with a Flake: | ||
+ | |||
+ | ```nix | ||
{ | { | ||
description = "Custom ARM image"; | description = "Custom ARM image"; | ||
− | inputs | + | |
− | + | inputs = { | |
+ | nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; | ||
+ | }; | ||
+ | |||
outputs = { self, nixpkgs }: { | outputs = { self, nixpkgs }: { | ||
− | nixosConfigurations. | + | nixosConfigurations.armimage = nixpkgs.lib.nixosSystem { |
− | system = "aarch64-linux"; # | + | system = "aarch64-linux"; # For building 64-bit ARM images |
modules = [ | modules = [ | ||
− | # Base SD image module for | + | # Base SD image module for your architecture |
"${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" | "${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" | ||
− | + | # Your customizations | |
− | # Your | ||
− | |||
− | |||
− | |||
({ ... }: { | ({ ... }: { | ||
+ | # Add your customizations here | ||
users.users.root.openssh.authorizedKeys.keys = [ | users.users.root.openssh.authorizedKeys.keys = [ | ||
− | "ssh-ed25519 | + | "ssh-ed25519 AAAAC3NzaC1lZDI1.... username@tld" |
]; | ]; | ||
− | |||
− | |||
}) | }) | ||
]; | ]; | ||
}; | }; | ||
− | + | ||
− | # Make the SD image the default | + | # Make the SD image the default output |
− | packages.aarch64-linux.default = | + | packages.aarch64-linux.default = |
− | self.nixosConfigurations. | + | self.nixosConfigurations.armimage.config.system.build.sdImage; |
}; | }; | ||
} | } | ||
− | Build | + | ``` |
+ | |||
+ | Build with: | ||
+ | |||
+ | ```bash | ||
+ | nix build | ||
+ | ``` | ||
− | + | #### Building the Traditional Way | |
− | + | For non-Flake builds: | |
− | |||
− | |||
− | |||
− | |||
− | + | ```bash | |
− | nix-build '<nixpkgs/nixos>' -A config.system.build.sdImage -I nixos-config=./sd-image | + | nix-build '<nixpkgs/nixos>' -A config.system.build.sdImage -I nixos-config=./sd-image.nix |
− | + | ``` | |
− | + | Where `sd-image.nix` contains: | |
− | + | ```nix | |
− | { | + | { ... }: { |
imports = [ | imports = [ | ||
− | + | <nixpkgs/nixos/modules/installer/sd-card/sd-image-aarch64.nix> | |
− | <nixpkgs/nixos/modules/installer/sd-card/sd-image-aarch64.nix> | ||
]; | ]; | ||
− | + | ||
# Your customizations here | # Your customizations here | ||
− | |||
users.users.root.openssh.authorizedKeys.keys = [ | users.users.root.openssh.authorizedKeys.keys = [ | ||
− | "ssh-ed25519 | + | "ssh-ed25519 AAAAC3NzaC1lZDI1.... username@tld" |
]; | ]; | ||
− | |||
− | |||
− | |||
} | } | ||
− | + | ``` | |
− | |||
− | + | ### U-Boot Customization | |
− | |||
− | + | Some boards may require custom U-Boot builds. For boards supported by upstream U-Boot, you can cross-compile from an x86_64 host: | |
− | |||
− | |||
− | |||
− | + | ```bash | |
− | nix-shell -p | + | nix-shell -p 'let plat = pkgsCross.aarch64-multiplatform; in plat.buildUBoot{defconfig = "board_defconfig"; extraMeta.platforms = ["aarch64-linux"];}' |
+ | ``` | ||
− | + | Replace `board_defconfig` with your board's U-Boot configuration name. | |
+ | |||
+ | For manual builds (when NixOS packages are unavailable): | ||
+ | |||
+ | ```bash | ||
+ | nix-shell -E 'with import <nixpkgs> {}; stdenv.mkDerivation { name = "arm-shell"; buildInputs = [git gnumake gcc gcc-arm-embedded dtc bison flex python3 swig]; }' | ||
git clone git://git.denx.de/u-boot.git | git clone git://git.denx.de/u-boot.git | ||
cd u-boot | cd u-boot | ||
− | + | make CROSS_COMPILE=arm-none-eabi- board_defconfig | |
− | + | make CROSS_COMPILE=arm-none-eabi- | |
− | make - | + | ``` |
− | |||
− | + | Flash the resulting U-Boot image to your storage device (SD card/eMMC/etc.) at the appropriate offset, typically: | |
− | |||
− | |||
− | |||
− | sudo dd if=u-boot- | + | ```bash |
− | + | sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdX bs=1024 seek=8 | |
− | + | ``` | |
− | + | The seek value and filename vary by board - check your board's wiki page or documentation. | |
− | # | + | ### Optimizing Performance on ARM |
+ | |||
+ | For better performance on ARM devices: | ||
+ | |||
+ | ```nix | ||
{ | { | ||
− | # CPU frequency scaling | + | # CPU frequency scaling (if supported by your hardware) |
− | |||
powerManagement.cpuFreqGovernor = "ondemand"; | powerManagement.cpuFreqGovernor = "ondemand"; | ||
− | + | ||
− | # IO scheduler tuning (improves SD card | + | # IO scheduler tuning (improves SD card performance) |
− | |||
services.udev.extraRules = '' | services.udev.extraRules = '' | ||
− | ACTION=="add|change", KERNEL=="mmcblk[0-9 | + | # Set deadline scheduler for SD cards |
+ | ACTION=="add|change", KERNEL=="mmcblk[0-9]", ATTR{queue/scheduler}="mq-deadline" | ||
''; | ''; | ||
− | + | ||
− | # | + | # Zram swap (better than SD card swap) |
zramSwap = { | zramSwap = { | ||
enable = true; | enable = true; | ||
− | algorithm = "zstd"; | + | algorithm = "zstd"; |
− | |||
}; | }; | ||
− | + | ||
− | + | # Using tmpfs for temp directories | |
− | # | ||
boot.tmpOnTmpfs = true; | boot.tmpOnTmpfs = true; | ||
} | } | ||
− | + | ``` | |
− | |||
− | + | ### Security Hardening for ARM Devices | |
− | + | Since NixOS 25.05, additional security features are available that are relevant for ARM devices: | |
+ | |||
+ | ```nix | ||
{ | { | ||
− | # | + | # Kernel hardening options |
boot.kernelParams = [ "slab_nomerge" "init_on_alloc=1" "init_on_free=1" ]; | boot.kernelParams = [ "slab_nomerge" "init_on_alloc=1" "init_on_free=1" ]; | ||
boot.kernel.sysctl = { | boot.kernel.sysctl = { | ||
− | "kernel.kptr_restrict" = | + | "kernel.kptr_restrict" = 2; |
− | "kernel.dmesg_restrict" = | + | "kernel.dmesg_restrict" = 1; |
− | |||
}; | }; | ||
− | + | ||
− | # | + | # Mandatory Access Control (if supported by your hardware) |
− | security.apparmor.enable = true; | + | security.apparmor.enable = true; # Less resource-intensive than SELinux |
− | + | ||
− | # | + | # Memory protection |
security.protectKernelImage = true; | security.protectKernelImage = true; | ||
+ | } | ||
+ | ``` | ||
− | + | Note: Always understand the security implications and compatibility impact of hardening options before applying them. Some settings may affect hardware compatibility or performance on specific ARM devices. | |
− | |||
− | + | ### Wayland Support on ARM64 | |
− | |||
− | |||
− | |||
− | |||
− | Wayland | + | Recent NixOS releases provide good Wayland support on ARM64 devices: |
− | |||
− | + | ```nix | |
− | |||
− | |||
{ | { | ||
− | # | + | # For GNOME |
+ | services.xserver = { | ||
+ | enable = true; | ||
+ | displayManager.gdm.enable = true; | ||
+ | desktopManager.gnome.enable = true; | ||
+ | displayManager.gdm.wayland = true; # Explicitly enable Wayland | ||
+ | }; | ||
+ | |||
+ | # For KDE Plasma | ||
+ | services.xserver = { | ||
+ | enable = true; | ||
+ | displayManager.sddm.enable = true; | ||
+ | desktopManager.plasma5.enable = true; | ||
+ | displayManager.defaultSession = "plasmawayland"; | ||
+ | }; | ||
+ | |||
+ | # Required for GPU acceleration | ||
hardware.opengl = { | hardware.opengl = { | ||
enable = true; | enable = true; | ||
driSupport = true; | driSupport = true; | ||
− | |||
}; | }; | ||
+ | } | ||
+ | ``` | ||
+ | |||
+ | For Raspberry Pi 5, add `dtoverlay=vc4-kms-v3d-pi5` in `/boot/config.txt` to enable GPU drivers compatible with Wayland. | ||
+ | |||
+ | ### Common ARM Device Troubleshooting | ||
+ | |||
+ | 1. **UART Console Access**: If your device isn't booting properly, enable UART: | ||
+ | ```nix | ||
+ | boot.kernelParams = [ | ||
+ | "console=ttyS0,115200n8" # Adjust ttyS0 to match your device's UART | ||
+ | ]; | ||
+ | ``` | ||
− | + | The actual device (ttyAMA0, ttyS0, ttyS1) depends on your hardware. See the [Enable UART](https://nixos.wiki/wiki/NixOS_on_ARM#Enable_UART) section for details. | |
− | + | ||
− | + | 2. **Memory Constraints**: For devices with limited RAM: | |
− | + | ```nix | |
− | + | nix.settings.cores = 1; # Limit parallel builds | |
− | + | nix.settings.max-jobs = 1; | |
− | + | boot.tmp.cleanOnBoot = true; # Clean /tmp on boot | |
− | + | ``` | |
+ | |||
+ | 3. **Network Issues**: For better wireless support: | ||
+ | ```nix | ||
+ | networking.networkmanager.enable = true; | ||
+ | hardware.enableRedistributableFirmware = true; # For WiFi firmware | ||
+ | ``` | ||
+ | |||
+ | ### Related Projects | ||
+ | |||
+ | #### NixOS Mobile | ||
+ | |||
+ | For mobile devices like smartphones and tablets, check out the [Mobile NixOS project](https://github.com/mobile-nixos/mobile-nixos). This project extends NixOS to run on devices like the PinePhone and select OnePlus models, with features specifically designed for mobile use cases. | ||
− | + | Mobile NixOS provides specialized modules for: | |
− | + | - Touch input and mobile-friendly UI | |
− | + | - Power management | |
− | + | - Cellular modem support | |
− | + | - Mobile hardware integration | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Visit the [Mobile NixOS documentation](https://mobile.nixos.org/) for detailed setup and configuration guidance. | |
− | |||
− | |||
− | ] | ||
− | |||
− | |||
− | # | + | #### QEMU Emulation |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | For detailed instructions on running NixOS ARM in QEMU, see the dedicated [NixOS on ARM/QEMU](https://nixos.wiki/wiki/NixOS_on_ARM/QEMU) wiki page. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | NixOS on ARM/QEMU: |
Revision as of 23:46, 4 April 2025
- NixOS installation & configuration
The installation image contains a MBR partition table with two partitions: a FAT16/32 `/boot` partition and an ext4 root filesystem. This design allows you to directly reuse the SD image's partition layout and "install" NixOS on the same storage device by replacing the default configuration and running `nixos-rebuild`. On first boot, the image automatically resizes the rootfs partition to utilize all available storage space.
NixOS on ARM supports two approaches to system configuration: traditional configuration.nix files and the now-standard Flakes approach (recommended since NixOS 25.05). Both methods are covered below.
- Boot Process Overview
All ARM boards in NixOS use U-Boot as the bootloader, alongside U-Boot's Generic Distro Configuration Concept to communicate boot information (kernel zImage path, initrd, DTB, command line arguments). U-Boot scans storage devices for `/extlinux/extlinux.conf` or `/boot/extlinux/extlinux.conf` files (generated by NixOS) and uses the bootable partition flag.
U-Boot provides both an interactive shell and generation selection menu (similar to GRUB). Board-specific input/display support varies - check your device's wiki page for details.
- Basic Setup with configuration.nix
To generate a default `/etc/nixos/configuration.nix` file and detect hardware:
```bash sudo nixos-generate-config ```
Here's a modern configuration template suitable for most ARM boards:
```nix { config, pkgs, lib, ... }:
{
# NixOS wants to enable GRUB by default boot.loader.grub.enable = false; # Enables the generation of /boot/extlinux/extlinux.conf boot.loader.generic-extlinux-compatible.enable = true; # Import hardware configuration generated by nixos-generate-config # This should correctly set up your file systems imports = [ ./hardware-configuration.nix ]; # File systems configuration (if not correctly detected) # Usually nixos-generate-config handles this properly, # but this is provided as a fallback /* fileSystems = { "/" = { device = "/dev/disk/by-label/NIXOS_SD"; fsType = "ext4"; }; }; */ # Adding a swap file is optional, but recommended for RAM-constrained devices # Size is in MiB # swapDevices = [ { device = "/swapfile"; size = 1024; } ]; # Enable basic networking networking.networkmanager.enable = true; # Basic firewall networking.firewall.enable = true; # Set your time zone time.timeZone = "UTC"; # System state version - IMPORTANT: Do not change this after initial install system.stateVersion = "25.05";
} ```
Apply your configuration with:
```bash sudo nixos-rebuild switch ```
- Kernel Selection
Kernel selection depends on your specific ARM device:
- **For boards with good mainline support** (including Raspberry Pi 4/5 on aarch64):
```nix boot.kernelPackages = pkgs.linuxPackages_latest; ```
- **For legacy Raspberry Pi models on armv6/armv7**:
```nix boot.kernelPackages = pkgs.linuxPackages_rpi; ```
- **For boards requiring specialized kernels**:
Refer to the board-specific wiki page for recommended kernel packages.
- Modern Flakes-Based Approach (Recommended)
Since NixOS 25.05, Flakes are the standard recommended approach for system configuration. They provide reproducible builds, locked dependencies, and simplify cross-device management.
1. Create a `flake.nix` file:
```nix {
description = "NixOS configuration for my ARM device"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; nixos-hardware.url = "github:NixOS/nixos-hardware"; }; outputs = { self, nixpkgs, nixos-hardware, ... }: { nixosConfigurations.mydevice = nixpkgs.lib.nixosSystem { system = "aarch64-linux"; # Or "armv6l-linux"/"armv7l-linux" for 32-bit ARM modules = [ # Hardware-specific modules (if available) # nixos-hardware.nixosModules.raspberry-pi-4 # nixos-hardware.nixosModules.raspberry-pi-5 # Your main configuration file ./configuration.nix ]; }; };
} ```
2. Apply your configuration with:
```bash sudo nixos-rebuild switch --flake .#mydevice ```
The `#mydevice` part corresponds to the `nixosConfigurations.mydevice` entry in your flake.nix file.
- Binary Caches
- AArch64 (ARM64)
The official NixOS Hydra instance builds full binary sets for the AArch64 architecture, available on https://cache.nixos.org for both the nixpkgs-unstable and stable channels:
```nix nix.settings = {
substituters = [ "https://cache.nixos.org" ]; trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
}; ```
- armv6l and armv7l (32-bit ARM)
There are currently no official binary caches for 32-bit ARM. Consider: - Building packages natively (slow on low-power devices) - Using cross-compilation (see below) - Setting up your own binary cache
For better performance on bandwidth-constrained devices, limit parallel connections:
```nix nix.settings = {
max-substitution-jobs = 2;
}; ```
- Building on Resource-Constrained ARM Devices
ARM devices, especially older or low-power models, may struggle with building large packages. Consider these approaches:
- 1. Remote Builders
Configure a more powerful machine (ideally ARM64) as a remote builder:
```nix nix.buildMachines = [{
hostName = "builder"; system = "aarch64-linux"; maxJobs = 4; speedFactor = 2; supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ];
}]; nix.distributedBuilds = true; ```
For setup instructions, see the [Distributed Build](https://nixos.wiki/wiki/Distributed_build) wiki page.
- 2. Cross-Compilation using QEMU with binfmt_misc
On an x86_64 NixOS system, enable ARM emulation:
```nix boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; ```
Then build ARM packages from your x86_64 machine using:
```bash nix build --system aarch64-linux .#package ```
Or for non-Flake commands:
```bash nix-build '<nixpkgs>' -A pkgs.hello --argstr system aarch64-linux ```
- 3. Building through QEMU/KVM
While slower than other methods, full emulation through QEMU/KVM is possible for ARM development. See the [NixOS on ARM/QEMU](https://nixos.wiki/wiki/NixOS_on_ARM/QEMU) page for detailed setup.
- Creating Custom ARM Images
- Building with Flakes (Recommended)
Create a custom SD card image with a Flake:
```nix {
description = "Custom ARM image"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; }; outputs = { self, nixpkgs }: { nixosConfigurations.armimage = nixpkgs.lib.nixosSystem { system = "aarch64-linux"; # For building 64-bit ARM images modules = [ # Base SD image module for your architecture "${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" # Your customizations ({ ... }: { # Add your customizations here users.users.root.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1.... username@tld" ]; }) ]; }; # Make the SD image the default output packages.aarch64-linux.default = self.nixosConfigurations.armimage.config.system.build.sdImage; };
} ```
Build with:
```bash nix build ```
- Building the Traditional Way
For non-Flake builds:
```bash nix-build '<nixpkgs/nixos>' -A config.system.build.sdImage -I nixos-config=./sd-image.nix ```
Where `sd-image.nix` contains:
```nix { ... }: {
imports = [ <nixpkgs/nixos/modules/installer/sd-card/sd-image-aarch64.nix> ]; # Your customizations here users.users.root.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1.... username@tld" ];
} ```
- U-Boot Customization
Some boards may require custom U-Boot builds. For boards supported by upstream U-Boot, you can cross-compile from an x86_64 host:
```bash nix-shell -p 'let plat = pkgsCross.aarch64-multiplatform; in plat.buildUBoot{defconfig = "board_defconfig"; extraMeta.platforms = ["aarch64-linux"];}' ```
Replace `board_defconfig` with your board's U-Boot configuration name.
For manual builds (when NixOS packages are unavailable):
```bash nix-shell -E 'with import <nixpkgs> {}; stdenv.mkDerivation { name = "arm-shell"; buildInputs = [git gnumake gcc gcc-arm-embedded dtc bison flex python3 swig]; }' git clone git://git.denx.de/u-boot.git cd u-boot make CROSS_COMPILE=arm-none-eabi- board_defconfig make CROSS_COMPILE=arm-none-eabi- ```
Flash the resulting U-Boot image to your storage device (SD card/eMMC/etc.) at the appropriate offset, typically:
```bash sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdX bs=1024 seek=8 ```
The seek value and filename vary by board - check your board's wiki page or documentation.
- Optimizing Performance on ARM
For better performance on ARM devices:
```nix {
# CPU frequency scaling (if supported by your hardware) powerManagement.cpuFreqGovernor = "ondemand"; # IO scheduler tuning (improves SD card performance) services.udev.extraRules = # Set deadline scheduler for SD cards ACTION=="add|change", KERNEL=="mmcblk[0-9]", ATTR{queue/scheduler}="mq-deadline" ; # Zram swap (better than SD card swap) zramSwap = { enable = true; algorithm = "zstd"; }; # Using tmpfs for temp directories boot.tmpOnTmpfs = true;
} ```
- Security Hardening for ARM Devices
Since NixOS 25.05, additional security features are available that are relevant for ARM devices:
```nix {
# Kernel hardening options boot.kernelParams = [ "slab_nomerge" "init_on_alloc=1" "init_on_free=1" ]; boot.kernel.sysctl = { "kernel.kptr_restrict" = 2; "kernel.dmesg_restrict" = 1; }; # Mandatory Access Control (if supported by your hardware) security.apparmor.enable = true; # Less resource-intensive than SELinux # Memory protection security.protectKernelImage = true;
} ```
Note: Always understand the security implications and compatibility impact of hardening options before applying them. Some settings may affect hardware compatibility or performance on specific ARM devices.
- Wayland Support on ARM64
Recent NixOS releases provide good Wayland support on ARM64 devices:
```nix {
# For GNOME services.xserver = { enable = true; displayManager.gdm.enable = true; desktopManager.gnome.enable = true; displayManager.gdm.wayland = true; # Explicitly enable Wayland }; # For KDE Plasma services.xserver = { enable = true; displayManager.sddm.enable = true; desktopManager.plasma5.enable = true; displayManager.defaultSession = "plasmawayland"; }; # Required for GPU acceleration hardware.opengl = { enable = true; driSupport = true; };
} ```
For Raspberry Pi 5, add `dtoverlay=vc4-kms-v3d-pi5` in `/boot/config.txt` to enable GPU drivers compatible with Wayland.
- Common ARM Device Troubleshooting
1. **UART Console Access**: If your device isn't booting properly, enable UART:
```nix boot.kernelParams = [ "console=ttyS0,115200n8" # Adjust ttyS0 to match your device's UART ]; ```
The actual device (ttyAMA0, ttyS0, ttyS1) depends on your hardware. See the [Enable UART](https://nixos.wiki/wiki/NixOS_on_ARM#Enable_UART) section for details.
2. **Memory Constraints**: For devices with limited RAM:
```nix nix.settings.cores = 1; # Limit parallel builds nix.settings.max-jobs = 1; boot.tmp.cleanOnBoot = true; # Clean /tmp on boot ```
3. **Network Issues**: For better wireless support:
```nix networking.networkmanager.enable = true; hardware.enableRedistributableFirmware = true; # For WiFi firmware ```
- Related Projects
- NixOS Mobile
For mobile devices like smartphones and tablets, check out the [Mobile NixOS project](https://github.com/mobile-nixos/mobile-nixos). This project extends NixOS to run on devices like the PinePhone and select OnePlus models, with features specifically designed for mobile use cases.
Mobile NixOS provides specialized modules for: - Touch input and mobile-friendly UI - Power management - Cellular modem support - Mobile hardware integration
Visit the [Mobile NixOS documentation](https://mobile.nixos.org/) for detailed setup and configuration guidance.
- QEMU Emulation
For detailed instructions on running NixOS ARM in QEMU, see the dedicated [NixOS on ARM/QEMU](https://nixos.wiki/wiki/NixOS_on_ARM/QEMU) wiki page.