Difference between revisions of "NixOS on ARM"
(Updated flake stable arm64 and mobile noxios suggetsions) |
(revision / format nixos flake update ARM) |
||
Line 1: | Line 1: | ||
− | + | Okay, let's treat this as the final review before submission. I'll format it correctly and incorporate the minor polish suggestions discussed previously. Assuming the content details are factually correct for your target (NixOS 25.05 in April 2025), here is the reviewed and formatted final version ready for the wiki: | |
− | The installation image contains | + | NixOS installation & configuration |
+ | The installation image typically contains partitions suitable for booting (e.g., FAT32 for /boot) and a root filesystem (e.g., ext4). The standard NixOS SD card images for ARM allow direct installation onto the storage device by modifying the configuration after the initial boot. On first boot, official images often resize the root filesystem to utilize available 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. | + | NixOS on ARM supports two main approaches to system configuration: traditional /etc/nixos/configuration.nix files and the now-standard Flakes approach (recommended since NixOS 25.05). Both methods are covered below. |
− | + | Boot Process Overview | |
+ | Most ARM boards supported by NixOS use U-Boot as the bootloader. NixOS utilizes U-Boot's Generic Distro Configuration Concept. U-Boot scans storage devices (like SD cards or eMMC) for /extlinux/extlinux.conf or /boot/extlinux/extlinux.conf. NixOS generates this file, which contains boot information (kernel path, initrd, device tree blob, command line arguments). U-Boot uses the partition marked as "bootable". | ||
− | + | U-Boot usually provides an interactive shell and often a generation selection menu (similar to GRUB). However, support for specific input (keyboard) or display devices during boot varies significantly by board – check your device's specific wiki page for details. | |
− | + | Basic Setup with configuration.nix | |
+ | This approach uses the traditional /etc/nixos/configuration.nix file. After booting the installation media (or the base system if installing directly onto the boot device): | ||
− | + | Mount your target filesystems under /mnt (e.g., mount /dev/disk/by-label/NIXOS_SD /mnt, mkdir /mnt/boot, mount /dev/disk/by-label/NIXOS_BOOT /mnt/boot). | |
+ | Generate a default configuration detecting your hardware: | ||
+ | Bash | ||
− | + | sudo nixos-generate-config --root /mnt | |
+ | Edit the generated /mnt/etc/nixos/configuration.nix. Here's a modern baseline template suitable for many ARM boards: | ||
+ | Nix | ||
− | + | # /mnt/etc/nixos/configuration.nix | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
{ config, pkgs, lib, ... }: | { config, pkgs, lib, ... }: | ||
{ | { | ||
− | # | + | imports = [ |
+ | # Include the results of hardware detection. | ||
+ | # This should correctly configure your detected file systems, LUKS (if used), etc. | ||
+ | ./hardware-configuration.nix | ||
+ | ]; | ||
+ | |||
+ | # Bootloader configuration for most ARM SBCs using U-Boot extlinux support | ||
boot.loader.grub.enable = false; | boot.loader.grub.enable = false; | ||
− | |||
− | |||
boot.loader.generic-extlinux-compatible.enable = true; | boot.loader.generic-extlinux-compatible.enable = true; | ||
− | + | ||
− | # | + | # Networking - Enable NetworkManager for easier setup (Wi-Fi & Ethernet) |
− | + | # Disable wait-for-online service if NetworkManager handles connections | |
− | |||
− | |||
− | |||
− | # | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
networking.networkmanager.enable = true; | networking.networkmanager.enable = true; | ||
− | + | systemd.services.NetworkManager-wait-online.enable = false; | |
− | # Basic firewall | + | |
+ | # Basic firewall (recommended) | ||
networking.firewall.enable = true; | networking.firewall.enable = true; | ||
− | + | ||
# Set your time zone | # Set your time zone | ||
− | time.timeZone = "UTC"; | + | time.timeZone = "UTC"; # Example: Replace with your time zone, e.g. "Europe/Amsterdam" |
− | + | ||
− | # System state version - IMPORTANT: Do not change this after initial install | + | # Add a swap file is optional, but recommended for RAM-constrained devices |
− | system.stateVersion = "25.05"; | + | # Size is in MiB. ZRAM (see performance section) is often preferred on SD cards. |
+ | # swapDevices = [ { device = "/swapfile"; size = 1024; } ]; | ||
+ | |||
+ | # Enable packages needed early, like firmware for Wi-Fi | ||
+ | # hardware.enableRedistributableFirmware = true; # Uncomment if needed for Wi-Fi/Bluetooth | ||
+ | |||
+ | # System state version - IMPORTANT: Set to the version you installed. | ||
+ | # Do not change this after initial install without understanding implications. | ||
+ | system.stateVersion = "25.05"; # Replace with your installed NixOS version | ||
+ | |||
+ | # Define users and basic packages in other parts of your configuration | ||
+ | # users.users.your_user = { ... }; | ||
+ | # environment.systemPackages = with pkgs; [ vim git ... ]; | ||
} | } | ||
− | + | Apply your configuration to the installed system using: | |
− | + | Bash | |
− | |||
sudo nixos-rebuild switch | sudo nixos-rebuild switch | ||
− | + | Kernel Selection | |
+ | Kernel selection depends on your specific ARM device. Add one of the following to your configuration.nix: | ||
− | + | For boards with good mainline Linux support (including Raspberry Pi 4/5 on aarch64): | |
+ | Nix | ||
− | + | boot.kernelPackages = pkgs.linuxPackages_latest; | |
+ | For legacy Raspberry Pi models (1/Zero/2/3) on armv6/armv7: | ||
+ | Nix | ||
− | + | boot.kernelPackages = pkgs.linuxPackages_rpi; | |
− | + | For boards requiring specialized Board Support Package (BSP) kernels: Refer to the board-specific wiki page or nixos-hardware repository for recommended kernel packages. | |
− | + | Modern Flakes-Based Approach (Recommended) | |
− | + | Since NixOS 25.05, Flakes are the standard recommended approach. They offer reproducible builds, locked dependencies via flake.lock, and simplify managing configurations across devices. | |
− | + | Enable Flakes: Ensure your configuration.nix includes: | |
− | |||
− | |||
− | |||
− | + | Nix | |
− | |||
− | + | nix.settings.experimental-features = [ "nix-command" "flakes" ]; | |
+ | (Rebuild once with nixos-rebuild switch if adding this for the first time). | ||
− | + | Create flake.nix: In your configuration directory (e.g., /etc/nixos/ or a git repository), create flake.nix: | |
− | + | Nix | |
− | + | # flake.nix | |
{ | { | ||
description = "NixOS configuration for my ARM device"; | description = "NixOS configuration for my ARM device"; | ||
− | + | ||
inputs = { | inputs = { | ||
+ | # Nixpkgs channel (e.g., stable, unstable) | ||
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; | nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; | ||
+ | |||
+ | # Hardware-specific quirks and configurations (optional but often helpful) | ||
nixos-hardware.url = "github:NixOS/nixos-hardware"; | nixos-hardware.url = "github:NixOS/nixos-hardware"; | ||
+ | |||
+ | # Home Manager for user dotfiles (optional) | ||
+ | # home-manager.url = "github:nix-community/home-manager"; | ||
+ | # home-manager.inputs.nixpkgs.follows = "nixpkgs"; | ||
}; | }; | ||
− | + | ||
− | outputs = { self, nixpkgs, nixos-hardware, ... }: { | + | outputs = { self, nixpkgs, nixos-hardware, ... }@inputs: { |
− | nixosConfigurations. | + | # Define your NixOS system configuration |
+ | nixosConfigurations.myhostname = nixpkgs.lib.nixosSystem { | ||
system = "aarch64-linux"; # Or "armv6l-linux"/"armv7l-linux" for 32-bit ARM | system = "aarch64-linux"; # Or "armv6l-linux"/"armv7l-linux" for 32-bit ARM | ||
+ | specialArgs = { inherit inputs; }; # Pass inputs to modules | ||
modules = [ | modules = [ | ||
− | # Hardware-specific modules (if | + | # Hardware-specific modules (select the correct one if needed) |
# 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 | ||
+ | |||
+ | # Home Manager module (optional) | ||
+ | # inputs.home-manager.nixosModules.home-manager { | ||
+ | # home-manager.useGlobalPkgs = true; | ||
+ | # home-manager.useUserPackages = true; | ||
+ | # home-manager.users.your_user = import ./home.nix; # User-specific config | ||
+ | # } | ||
]; | ]; | ||
}; | }; | ||
}; | }; | ||
} | } | ||
− | + | Apply Configuration: From within the directory containing flake.nix: | |
− | + | Bash | |
− | + | sudo nixos-rebuild switch --flake .#myhostname | |
− | sudo nixos-rebuild switch --flake .# | + | (Replace myhostname with the actual key used in nixosConfigurations above). |
− | |||
− | The | + | Binary Caches |
+ | AArch64 (ARM64) | ||
+ | The official NixOS Hydra instance builds extensive binary sets for AArch64, available from the default cache https://cache.nixos.org. Ensure your configuration includes: | ||
− | + | Nix | |
− | # | + | # configuration.nix or equivalent module |
− | |||
− | |||
− | |||
nix.settings = { | nix.settings = { | ||
substituters = [ | substituters = [ | ||
"https://cache.nixos.org" | "https://cache.nixos.org" | ||
+ | # Add other community caches if desired, e.g., cachix caches | ||
]; | ]; | ||
trusted-public-keys = [ | trusted-public-keys = [ | ||
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" | "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" | ||
+ | # Add public keys for other caches | ||
]; | ]; | ||
}; | }; | ||
− | + | armv6l and armv7l (32-bit ARM) | |
+ | There are currently no official binary caches for 32-bit ARM platforms. Builds will happen natively (which can be very slow on constrained devices) or via emulation/cross-compilation. Consider: | ||
− | + | Using a more powerful machine for builds (see Remote Builders/Cross-Compilation below). | |
− | + | Setting up your own binary cache (e.g., using cachix or nix-serve) if building frequently or for multiple devices. | |
− | + | For potentially faster downloads on slow or unstable connections (at the cost of higher peak bandwidth), you can adjust substitution jobs: | |
− | |||
− | |||
− | + | Nix | |
− | + | # nix.settings continuation | |
nix.settings = { | nix.settings = { | ||
− | max-substitution-jobs = | + | # ... other settings |
+ | max-substitution-jobs = 4; # Default is usually higher, adjust as needed | ||
}; | }; | ||
− | + | Building on Resource-Constrained ARM Devices | |
− | + | Building large packages (browsers, compilers) directly on low-power ARM devices can take hours or days. Use these strategies: | |
− | |||
− | |||
− | |||
− | + | Remote Builders: Configure a more powerful machine (x86_64 or ideally another ARM64 machine) to perform builds remotely. | |
− | + | Nix | |
− | + | # configuration.nix on the ARM device | |
nix.buildMachines = [{ | nix.buildMachines = [{ | ||
− | hostName = "builder"; | + | hostName = "powerful-builder.local"; # Address of the builder machine |
− | system = "aarch64-linux"; | + | system = "aarch64-linux"; # Or "x86_64-linux" |
− | maxJobs = | + | maxJobs = 8; # Number of jobs the builder can handle |
− | speedFactor = | + | speedFactor = 4; # Relative speed estimate |
supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ]; | supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ]; | ||
+ | # Needed if builder is different architecture: | ||
+ | # requires = [ "emulation" ]; # If builder emulates ARM via binfmt_misc | ||
}]; | }]; | ||
nix.distributedBuilds = true; | nix.distributedBuilds = true; | ||
− | + | See the Distributed Build wiki page for detailed setup. | |
− | |||
− | |||
− | + | Cross-Compilation using QEMU (binfmt_misc): Build ARM packages on an x86_64 NixOS machine using QEMU user-space emulation. Enable on the x86_64 builder: | |
− | + | Nix | |
− | + | # configuration.nix on the x86_64 builder | |
− | boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; | + | boot.binfmt.emulatedSystems = [ "aarch64-linux" "armv7l-linux" ]; |
− | + | Then build specifically for ARM from the x86_64 machine: | |
− | + | Bash | |
− | + | # Build a Flake package for ARM | |
− | nix build --system aarch64-linux .# | + | nix build --system aarch64-linux .#somePackage |
− | |||
− | + | # Build a legacy package for ARM | |
− | |||
− | |||
nix-build '<nixpkgs>' -A pkgs.hello --argstr system aarch64-linux | nix-build '<nixpkgs>' -A pkgs.hello --argstr system aarch64-linux | ||
− | + | Note: Emulation is significantly slower than native compilation. | |
− | + | Full QEMU/KVM Emulation: Slower still, but possible for development. See the NixOS on ARM/QEMU page. | |
− | + | Creating Custom ARM Images | |
+ | Generate your own bootable SD card images with custom configurations included. | ||
− | + | Building with Flakes (Recommended) | |
+ | Nix | ||
− | # | + | # flake.nix (example for building an image) |
− | |||
− | |||
− | |||
− | |||
{ | { | ||
description = "Custom ARM image"; | description = "Custom ARM image"; | ||
− | + | inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; | |
− | inputs | + | |
− | |||
− | |||
− | |||
outputs = { self, nixpkgs }: { | outputs = { self, nixpkgs }: { | ||
− | nixosConfigurations. | + | nixosConfigurations.myarmimage = nixpkgs.lib.nixosSystem { |
− | system = "aarch64-linux"; # | + | system = "aarch64-linux"; # Target architecture for the image |
modules = [ | modules = [ | ||
− | # Base SD image module for | + | # Base SD image module for the target architecture |
"${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" | "${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" | ||
− | # Your | + | |
+ | # Your custom configuration module(s) | ||
+ | ./my-image-configuration.nix # Contains users, packages, services etc. | ||
+ | |||
+ | # Example inline customization: Pre-add SSH keys | ||
({ ... }: { | ({ ... }: { | ||
− | |||
users.users.root.openssh.authorizedKeys.keys = [ | users.users.root.openssh.authorizedKeys.keys = [ | ||
− | "ssh-ed25519 | + | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA..." |
]; | ]; | ||
+ | # Ensure SSH service is enabled in your main config | ||
+ | services.openssh.enable = true; | ||
}) | }) | ||
]; | ]; | ||
}; | }; | ||
− | + | ||
− | # Make the SD image the default output | + | # Make the SD image the default build output for `nix build` |
− | packages.aarch64-linux.default = | + | packages.aarch64-linux.default = |
− | self.nixosConfigurations. | + | self.nixosConfigurations.myarmimage.config.system.build.sdImage; |
}; | }; | ||
} | } | ||
− | + | Build the image (will produce ./result/sd-image/*.img): | |
− | |||
− | Build | ||
− | |||
− | |||
− | |||
− | |||
− | + | Bash | |
− | + | nix build .#myarmimage.config.system.build.sdImage | |
+ | # Or if using the default package output: | ||
+ | # nix build | ||
+ | Building the Traditional Way | ||
+ | Bash | ||
− | + | # Command line build | |
− | nix-build '<nixpkgs/nixos>' -A config.system.build.sdImage -I nixos-config=./sd-image.nix | + | nix-build '<nixpkgs/nixos>' -A config.system.build.sdImage -I nixos-config=./sd-image-config.nix |
− | + | Where sd-image-config.nix contains: | |
− | + | Nix | |
− | + | # sd-image-config.nix | |
− | { ... }: { | + | { config, pkgs, ... }: { |
imports = [ | imports = [ | ||
− | <nixpkgs/nixos/modules/installer/sd-card/sd-image-aarch64.nix> | + | # Base SD image module |
+ | <nixpkgs/nixos/modules/installer/sd-card/sd-image-aarch64.nix> # Adjust arch if needed | ||
]; | ]; | ||
− | + | ||
# Your customizations here | # Your customizations here | ||
+ | system.stateVersion = "25.05"; # Match nixpkgs version | ||
users.users.root.openssh.authorizedKeys.keys = [ | users.users.root.openssh.authorizedKeys.keys = [ | ||
− | "ssh-ed25519 | + | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA..." |
]; | ]; | ||
+ | services.openssh.enable = true; | ||
+ | environment.systemPackages = with pkgs; [ vim htop ]; | ||
+ | # ... other config ... | ||
} | } | ||
− | + | U-Boot Customization | |
+ | While NixOS aims for mainline U-Boot compatibility, some boards might require specific U-Boot versions or configurations. | ||
− | + | Building via Nixpkgs (if board defconfig exists): | |
+ | Bash | ||
− | + | # Example for AArch64, replace defconfig | |
+ | nix-shell -p 'let plat = pkgsCross.aarch64-multiplatform; in plat.buildUBoot { defconfig = "myboard_defconfig"; extraMeta.platforms = ["aarch64-linux"]; }' | ||
+ | Manual Build (using Nix shell for toolchain): | ||
+ | Bash | ||
− | + | # Get build environment | |
− | nix-shell -p | + | nix-shell -p git gnumake gcc gcc-arm-embedded dtc bison flex python3 swig --run "bash" |
− | |||
− | + | # Inside the nix-shell: | |
− | |||
− | |||
− | |||
− | |||
− | nix-shell | ||
git clone git://git.denx.de/u-boot.git | git clone git://git.denx.de/u-boot.git | ||
cd u-boot | cd u-boot | ||
− | + | export CROSS_COMPILE=arm-none-eabi- # Adjust toolchain prefix if needed | |
− | make | + | make myboard_defconfig |
− | + | make -j$(nproc) | |
+ | cd .. # U-Boot binary (e.g., u-boot-sunxi-with-spl.bin) is in u-boot/ | ||
− | + | # Exit nix-shell | |
+ | exit | ||
+ | Flashing U-Boot: This is board-specific and dangerous if done incorrectly. Consult your board's documentation. A common pattern (e.g., for Allwinner) but verify for your specific board: | ||
+ | Bash | ||
− | + | sudo dd if=u-boot-binary.bin of=/dev/sdX bs=1024 seek=8 # EXAMPLE ONLY! Verify offset and device! | |
− | sudo dd if=u-boot- | + | Optimizing Performance on ARM |
− | + | Tune your system for better responsiveness, especially on lower-power devices: | |
− | + | Nix | |
− | # | + | # configuration.nix |
− | |||
− | |||
− | |||
− | |||
{ | { | ||
− | # CPU frequency scaling (if supported | + | # CPU frequency scaling governor (if supported) |
+ | # 'ondemand' or 'schedutil' are common defaults. 'performance' uses max clock speed. | ||
powerManagement.cpuFreqGovernor = "ondemand"; | powerManagement.cpuFreqGovernor = "ondemand"; | ||
− | + | ||
− | # IO scheduler tuning (improves SD card performance) | + | # IO scheduler tuning (improves SD card/eMMC performance) |
+ | # mq-deadline is often a good choice for flash storage. | ||
services.udev.extraRules = '' | services.udev.extraRules = '' | ||
− | + | ACTION=="add|change", KERNEL=="mmcblk[0-9]|sd[a-z]", ATTR{queue/scheduler}="mq-deadline" | |
− | ACTION=="add|change", KERNEL=="mmcblk[0-9]", ATTR{queue/scheduler}="mq-deadline" | ||
''; | ''; | ||
− | + | ||
− | # | + | # ZRAM Swap (compressed RAM swap - highly recommended over SD card swap) |
zramSwap = { | zramSwap = { | ||
enable = true; | enable = true; | ||
− | algorithm = "zstd"; | + | algorithm = "zstd"; # Fast compression |
+ | # memoryPercent = 50; # Optional: Adjust percentage of RAM to use | ||
}; | }; | ||
− | + | # Ensure traditional swap files/partitions are disabled if using ZRAM primarily. | |
− | # | + | |
+ | # Use tmpfs for /tmp (reduces SD card writes) | ||
boot.tmpOnTmpfs = true; | boot.tmpOnTmpfs = true; | ||
} | } | ||
− | + | Security Hardening for ARM Devices | |
+ | Consider these options, especially for devices exposed to networks: | ||
− | + | Nix | |
− | + | # configuration.nix | |
− | |||
− | |||
{ | { | ||
− | # Kernel hardening | + | # Example Kernel hardening parameters |
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" = 2; | + | "kernel.kptr_restrict" = "2"; |
− | "kernel.dmesg_restrict" = 1; | + | "kernel.dmesg_restrict" = "1"; |
+ | # Add other sysctl hardening options here | ||
}; | }; | ||
− | + | ||
− | # Mandatory Access Control ( | + | # Enable AppArmor Mandatory Access Control (generally lower overhead than SELinux) |
− | security.apparmor.enable = true; | + | security.apparmor.enable = true; |
− | + | ||
− | # | + | # Protect kernel image in memory |
security.protectKernelImage = true; | security.protectKernelImage = true; | ||
+ | |||
+ | # Basic firewall enabled previously is a good start. Configure rules as needed: | ||
+ | # networking.firewall.allowedTCPPorts = [ 22 ]; # Example: Allow SSH | ||
+ | |||
+ | # Consider enabling auditd for logging security events | ||
+ | # security.audit.enable = true; | ||
+ | # security.auditd.enable = true; | ||
} | } | ||
− | + | Note: Always research the implications of hardening options. Some may affect performance or compatibility with specific hardware or software. Apply incrementally. | |
− | + | Wayland Support on ARM64 | |
+ | For graphical desktops, Wayland generally offers better performance on modern ARM64 devices: | ||
− | + | Nix | |
− | + | # configuration.nix | |
+ | { | ||
+ | # Enable OpenGL drivers (essential) | ||
+ | hardware.opengl = { | ||
+ | enable = true; | ||
+ | driSupport = true; | ||
+ | # driSupport32Bit = true; # If running 32-bit apps via emulation | ||
+ | }; | ||
− | + | # Example for GNOME (Wayland by default) | |
− | |||
− | # | ||
services.xserver = { | services.xserver = { | ||
enable = true; | enable = true; | ||
displayManager.gdm.enable = true; | displayManager.gdm.enable = true; | ||
desktopManager.gnome.enable = true; | desktopManager.gnome.enable = true; | ||
− | + | # Optional: Explicitly prefer Wayland, though usually default | |
− | + | # displayManager.gdm.wayland = true; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
}; | }; | ||
+ | |||
+ | # Example for KDE Plasma (Choose Wayland session at login) | ||
+ | # services.xserver = { | ||
+ | # enable = true; | ||
+ | # displayManager.sddm.enable = true; | ||
+ | # desktopManager.plasma5.enable = true; | ||
+ | # # Ensure sddm offers Wayland session (usually default) | ||
+ | # }; | ||
} | } | ||
− | + | Raspberry Pi 4/5 Specific: Ensure appropriate GPU overlays are enabled in /boot/config.txt (NixOS modules might handle this, but verify). For RPi 5 Wayland, dtoverlay=vc4-kms-v3d-pi5 is often needed. Check nixos-hardware configs. | |
+ | Common ARM Device Troubleshooting | ||
+ | UART Console Access: If the device doesn't boot fully or display video, connect a USB-to-Serial adapter to the board's UART pins. Add kernel parameters to enable console output (adjust device ttyS0/ttyAMA0 and speed 115200 based on board): | ||
+ | Nix | ||
− | + | # configuration.nix | |
+ | boot.kernelParams = [ | ||
+ | "console=ttyS0,115200n8" # Example, check board specifics | ||
+ | ]; | ||
+ | Memory Constraints (Low RAM devices): Limit build resource usage: | ||
+ | Nix | ||
− | # | + | # configuration.nix |
− | + | nix.settings = { | |
− | + | cores = 1; # Limit parallel builds locally | |
− | + | max-jobs = 1; | |
− | + | }; | |
− | + | boot.tmp.cleanOnBoot = true; # Free up /tmp space on reboot | |
− | + | (Use ZRAM swap, avoid heavy desktop environments). | |
− | + | Network Issues (Wi-Fi/Bluetooth): Ensure necessary firmware is enabled: | |
− | + | Nix | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | For | + | # configuration.nix |
+ | hardware.enableRedistributableFirmware = true; # For many common Wi-Fi/BT chips | ||
+ | # Some firmware might need specific enabling, e.g.: | ||
+ | # hardware.firmware = [ pkgs.wireless-regdb ]; | ||
+ | Ensure networking.networkmanager.enable = true; for easier Wi-Fi management. | ||
+ | Related Projects | ||
+ | Mobile NixOS: Extends NixOS to run on mobile devices like the PinePhone. Provides modules for mobile-specific hardware and UI needs. See Mobile NixOS project. | ||
+ | NixOS Hardware: A repository of hardware-specific configurations and workarounds for various devices, including many ARM boards. See NixOS/nixos-hardware. | ||
+ | NixOS on ARM/QEMU: Instructions for running NixOS ARM builds within QEMU for development and testing. See the NixOS on ARM/QEMU wiki page. |
Revision as of 10:09, 2 April 2025
Okay, let's treat this as the final review before submission. I'll format it correctly and incorporate the minor polish suggestions discussed previously. Assuming the content details are factually correct for your target (NixOS 25.05 in April 2025), here is the reviewed and formatted final version ready for the wiki:
NixOS installation & configuration The installation image typically contains partitions suitable for booting (e.g., FAT32 for /boot) and a root filesystem (e.g., ext4). The standard NixOS SD card images for ARM allow direct installation onto the storage device by modifying the configuration after the initial boot. On first boot, official images often resize the root filesystem to utilize available space.
NixOS on ARM supports two main approaches to system configuration: traditional /etc/nixos/configuration.nix files and the now-standard Flakes approach (recommended since NixOS 25.05). Both methods are covered below.
Boot Process Overview Most ARM boards supported by NixOS use U-Boot as the bootloader. NixOS utilizes U-Boot's Generic Distro Configuration Concept. U-Boot scans storage devices (like SD cards or eMMC) for /extlinux/extlinux.conf or /boot/extlinux/extlinux.conf. NixOS generates this file, which contains boot information (kernel path, initrd, device tree blob, command line arguments). U-Boot uses the partition marked as "bootable".
U-Boot usually provides an interactive shell and often a generation selection menu (similar to GRUB). However, support for specific input (keyboard) or display devices during boot varies significantly by board – check your device's specific wiki page for details.
Basic Setup with configuration.nix This approach uses the traditional /etc/nixos/configuration.nix file. After booting the installation media (or the base system if installing directly onto the boot device):
Mount your target filesystems under /mnt (e.g., mount /dev/disk/by-label/NIXOS_SD /mnt, mkdir /mnt/boot, mount /dev/disk/by-label/NIXOS_BOOT /mnt/boot). Generate a default configuration detecting your hardware: Bash
sudo nixos-generate-config --root /mnt Edit the generated /mnt/etc/nixos/configuration.nix. Here's a modern baseline template suitable for many ARM boards: Nix
- /mnt/etc/nixos/configuration.nix
{ config, pkgs, lib, ... }:
{
imports = [ # Include the results of hardware detection. # This should correctly configure your detected file systems, LUKS (if used), etc. ./hardware-configuration.nix ];
# Bootloader configuration for most ARM SBCs using U-Boot extlinux support boot.loader.grub.enable = false; boot.loader.generic-extlinux-compatible.enable = true;
# Networking - Enable NetworkManager for easier setup (Wi-Fi & Ethernet) # Disable wait-for-online service if NetworkManager handles connections networking.networkmanager.enable = true; systemd.services.NetworkManager-wait-online.enable = false;
# Basic firewall (recommended) networking.firewall.enable = true;
# Set your time zone time.timeZone = "UTC"; # Example: Replace with your time zone, e.g. "Europe/Amsterdam"
# Add a swap file is optional, but recommended for RAM-constrained devices # Size is in MiB. ZRAM (see performance section) is often preferred on SD cards. # swapDevices = [ { device = "/swapfile"; size = 1024; } ];
# Enable packages needed early, like firmware for Wi-Fi # hardware.enableRedistributableFirmware = true; # Uncomment if needed for Wi-Fi/Bluetooth
# System state version - IMPORTANT: Set to the version you installed. # Do not change this after initial install without understanding implications. system.stateVersion = "25.05"; # Replace with your installed NixOS version
# Define users and basic packages in other parts of your configuration # users.users.your_user = { ... }; # environment.systemPackages = with pkgs; [ vim git ... ];
} Apply your configuration to the installed system using:
Bash
sudo nixos-rebuild switch Kernel Selection Kernel selection depends on your specific ARM device. Add one of the following to your configuration.nix:
For boards with good mainline Linux support (including Raspberry Pi 4/5 on aarch64): Nix
boot.kernelPackages = pkgs.linuxPackages_latest; For legacy Raspberry Pi models (1/Zero/2/3) on armv6/armv7: Nix
boot.kernelPackages = pkgs.linuxPackages_rpi; For boards requiring specialized Board Support Package (BSP) kernels: Refer to the board-specific wiki page or nixos-hardware repository for recommended kernel packages. Modern Flakes-Based Approach (Recommended) Since NixOS 25.05, Flakes are the standard recommended approach. They offer reproducible builds, locked dependencies via flake.lock, and simplify managing configurations across devices.
Enable Flakes: Ensure your configuration.nix includes:
Nix
nix.settings.experimental-features = [ "nix-command" "flakes" ]; (Rebuild once with nixos-rebuild switch if adding this for the first time).
Create flake.nix: In your configuration directory (e.g., /etc/nixos/ or a git repository), create flake.nix:
Nix
- flake.nix
{
description = "NixOS configuration for my ARM device";
inputs = { # Nixpkgs channel (e.g., stable, unstable) nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
# Hardware-specific quirks and configurations (optional but often helpful) nixos-hardware.url = "github:NixOS/nixos-hardware";
# Home Manager for user dotfiles (optional) # home-manager.url = "github:nix-community/home-manager"; # home-manager.inputs.nixpkgs.follows = "nixpkgs"; };
outputs = { self, nixpkgs, nixos-hardware, ... }@inputs: { # Define your NixOS system configuration nixosConfigurations.myhostname = nixpkgs.lib.nixosSystem { system = "aarch64-linux"; # Or "armv6l-linux"/"armv7l-linux" for 32-bit ARM specialArgs = { inherit inputs; }; # Pass inputs to modules modules = [ # Hardware-specific modules (select the correct one if needed) # nixos-hardware.nixosModules.raspberry-pi-4 # nixos-hardware.nixosModules.raspberry-pi-5
# Your main configuration file ./configuration.nix
# Home Manager module (optional) # inputs.home-manager.nixosModules.home-manager { # home-manager.useGlobalPkgs = true; # home-manager.useUserPackages = true; # home-manager.users.your_user = import ./home.nix; # User-specific config # } ]; }; };
} Apply Configuration: From within the directory containing flake.nix:
Bash
sudo nixos-rebuild switch --flake .#myhostname (Replace myhostname with the actual key used in nixosConfigurations above).
Binary Caches AArch64 (ARM64) The official NixOS Hydra instance builds extensive binary sets for AArch64, available from the default cache https://cache.nixos.org. Ensure your configuration includes:
Nix
- configuration.nix or equivalent module
nix.settings = {
substituters = [ "https://cache.nixos.org" # Add other community caches if desired, e.g., cachix caches ]; trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" # Add public keys for other caches ];
}; armv6l and armv7l (32-bit ARM) There are currently no official binary caches for 32-bit ARM platforms. Builds will happen natively (which can be very slow on constrained devices) or via emulation/cross-compilation. Consider:
Using a more powerful machine for builds (see Remote Builders/Cross-Compilation below). Setting up your own binary cache (e.g., using cachix or nix-serve) if building frequently or for multiple devices. For potentially faster downloads on slow or unstable connections (at the cost of higher peak bandwidth), you can adjust substitution jobs:
Nix
- nix.settings continuation
nix.settings = {
# ... other settings max-substitution-jobs = 4; # Default is usually higher, adjust as needed
}; Building on Resource-Constrained ARM Devices Building large packages (browsers, compilers) directly on low-power ARM devices can take hours or days. Use these strategies:
Remote Builders: Configure a more powerful machine (x86_64 or ideally another ARM64 machine) to perform builds remotely.
Nix
- configuration.nix on the ARM device
nix.buildMachines = [{
hostName = "powerful-builder.local"; # Address of the builder machine system = "aarch64-linux"; # Or "x86_64-linux" maxJobs = 8; # Number of jobs the builder can handle speedFactor = 4; # Relative speed estimate supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ]; # Needed if builder is different architecture: # requires = [ "emulation" ]; # If builder emulates ARM via binfmt_misc
}]; nix.distributedBuilds = true; See the Distributed Build wiki page for detailed setup.
Cross-Compilation using QEMU (binfmt_misc): Build ARM packages on an x86_64 NixOS machine using QEMU user-space emulation. Enable on the x86_64 builder:
Nix
- configuration.nix on the x86_64 builder
boot.binfmt.emulatedSystems = [ "aarch64-linux" "armv7l-linux" ]; Then build specifically for ARM from the x86_64 machine:
Bash
- Build a Flake package for ARM
nix build --system aarch64-linux .#somePackage
- Build a legacy package for ARM
nix-build '<nixpkgs>' -A pkgs.hello --argstr system aarch64-linux Note: Emulation is significantly slower than native compilation.
Full QEMU/KVM Emulation: Slower still, but possible for development. See the NixOS on ARM/QEMU page.
Creating Custom ARM Images Generate your own bootable SD card images with custom configurations included.
Building with Flakes (Recommended) Nix
- flake.nix (example for building an image)
{
description = "Custom ARM image"; inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05";
outputs = { self, nixpkgs }: { nixosConfigurations.myarmimage = nixpkgs.lib.nixosSystem { system = "aarch64-linux"; # Target architecture for the image modules = [ # Base SD image module for the target architecture "${nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix"
# Your custom configuration module(s) ./my-image-configuration.nix # Contains users, packages, services etc.
# Example inline customization: Pre-add SSH keys ({ ... }: { users.users.root.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA..." ]; # Ensure SSH service is enabled in your main config services.openssh.enable = true; }) ]; };
# Make the SD image the default build output for `nix build` packages.aarch64-linux.default = self.nixosConfigurations.myarmimage.config.system.build.sdImage; };
} Build the image (will produce ./result/sd-image/*.img):
Bash
nix build .#myarmimage.config.system.build.sdImage
- Or if using the default package output:
- nix build
Building the Traditional Way Bash
- Command line build
nix-build '<nixpkgs/nixos>' -A config.system.build.sdImage -I nixos-config=./sd-image-config.nix Where sd-image-config.nix contains:
Nix
- sd-image-config.nix
{ config, pkgs, ... }: {
imports = [ # Base SD image module <nixpkgs/nixos/modules/installer/sd-card/sd-image-aarch64.nix> # Adjust arch if needed ];
# Your customizations here system.stateVersion = "25.05"; # Match nixpkgs version users.users.root.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA..." ]; services.openssh.enable = true; environment.systemPackages = with pkgs; [ vim htop ]; # ... other config ...
} U-Boot Customization While NixOS aims for mainline U-Boot compatibility, some boards might require specific U-Boot versions or configurations.
Building via Nixpkgs (if board defconfig exists): Bash
- Example for AArch64, replace defconfig
nix-shell -p 'let plat = pkgsCross.aarch64-multiplatform; in plat.buildUBoot { defconfig = "myboard_defconfig"; extraMeta.platforms = ["aarch64-linux"]; }' Manual Build (using Nix shell for toolchain): Bash
- Get build environment
nix-shell -p git gnumake gcc gcc-arm-embedded dtc bison flex python3 swig --run "bash"
- Inside the nix-shell:
git clone git://git.denx.de/u-boot.git cd u-boot export CROSS_COMPILE=arm-none-eabi- # Adjust toolchain prefix if needed make myboard_defconfig make -j$(nproc) cd .. # U-Boot binary (e.g., u-boot-sunxi-with-spl.bin) is in u-boot/
- Exit nix-shell
exit Flashing U-Boot: This is board-specific and dangerous if done incorrectly. Consult your board's documentation. A common pattern (e.g., for Allwinner) but verify for your specific board: Bash
sudo dd if=u-boot-binary.bin of=/dev/sdX bs=1024 seek=8 # EXAMPLE ONLY! Verify offset and device! Optimizing Performance on ARM Tune your system for better responsiveness, especially on lower-power devices:
Nix
- configuration.nix
{
# CPU frequency scaling governor (if supported) # 'ondemand' or 'schedutil' are common defaults. 'performance' uses max clock speed. powerManagement.cpuFreqGovernor = "ondemand";
# IO scheduler tuning (improves SD card/eMMC performance) # mq-deadline is often a good choice for flash storage. services.udev.extraRules = ACTION=="add|change", KERNEL=="mmcblk[0-9]|sd[a-z]", ATTR{queue/scheduler}="mq-deadline" ;
# ZRAM Swap (compressed RAM swap - highly recommended over SD card swap) zramSwap = { enable = true; algorithm = "zstd"; # Fast compression # memoryPercent = 50; # Optional: Adjust percentage of RAM to use }; # Ensure traditional swap files/partitions are disabled if using ZRAM primarily.
# Use tmpfs for /tmp (reduces SD card writes) boot.tmpOnTmpfs = true;
} Security Hardening for ARM Devices Consider these options, especially for devices exposed to networks:
Nix
- configuration.nix
{
# Example Kernel hardening parameters boot.kernelParams = [ "slab_nomerge" "init_on_alloc=1" "init_on_free=1" ]; boot.kernel.sysctl = { "kernel.kptr_restrict" = "2"; "kernel.dmesg_restrict" = "1"; # Add other sysctl hardening options here };
# Enable AppArmor Mandatory Access Control (generally lower overhead than SELinux) security.apparmor.enable = true;
# Protect kernel image in memory security.protectKernelImage = true;
# Basic firewall enabled previously is a good start. Configure rules as needed: # networking.firewall.allowedTCPPorts = [ 22 ]; # Example: Allow SSH
# Consider enabling auditd for logging security events # security.audit.enable = true; # security.auditd.enable = true;
} Note: Always research the implications of hardening options. Some may affect performance or compatibility with specific hardware or software. Apply incrementally.
Wayland Support on ARM64 For graphical desktops, Wayland generally offers better performance on modern ARM64 devices:
Nix
- configuration.nix
{
# Enable OpenGL drivers (essential) hardware.opengl = { enable = true; driSupport = true; # driSupport32Bit = true; # If running 32-bit apps via emulation };
# Example for GNOME (Wayland by default) services.xserver = { enable = true; displayManager.gdm.enable = true; desktopManager.gnome.enable = true; # Optional: Explicitly prefer Wayland, though usually default # displayManager.gdm.wayland = true; };
# Example for KDE Plasma (Choose Wayland session at login) # services.xserver = { # enable = true; # displayManager.sddm.enable = true; # desktopManager.plasma5.enable = true; # # Ensure sddm offers Wayland session (usually default) # };
} Raspberry Pi 4/5 Specific: Ensure appropriate GPU overlays are enabled in /boot/config.txt (NixOS modules might handle this, but verify). For RPi 5 Wayland, dtoverlay=vc4-kms-v3d-pi5 is often needed. Check nixos-hardware configs. Common ARM Device Troubleshooting UART Console Access: If the device doesn't boot fully or display video, connect a USB-to-Serial adapter to the board's UART pins. Add kernel parameters to enable console output (adjust device ttyS0/ttyAMA0 and speed 115200 based on board): Nix
- configuration.nix
boot.kernelParams = [
"console=ttyS0,115200n8" # Example, check board specifics
]; Memory Constraints (Low RAM devices): Limit build resource usage: Nix
- configuration.nix
nix.settings = {
cores = 1; # Limit parallel builds locally max-jobs = 1;
}; boot.tmp.cleanOnBoot = true; # Free up /tmp space on reboot (Use ZRAM swap, avoid heavy desktop environments). Network Issues (Wi-Fi/Bluetooth): Ensure necessary firmware is enabled: Nix
- configuration.nix
hardware.enableRedistributableFirmware = true; # For many common Wi-Fi/BT chips
- Some firmware might need specific enabling, e.g.:
- hardware.firmware = [ pkgs.wireless-regdb ];
Ensure networking.networkmanager.enable = true; for easier Wi-Fi management. Related Projects Mobile NixOS: Extends NixOS to run on mobile devices like the PinePhone. Provides modules for mobile-specific hardware and UI needs. See Mobile NixOS project. NixOS Hardware: A repository of hardware-specific configurations and workarounds for various devices, including many ARM boards. See NixOS/nixos-hardware. NixOS on ARM/QEMU: Instructions for running NixOS ARM builds within QEMU for development and testing. See the NixOS on ARM/QEMU wiki page.