This represents the first step in being able to build ready-to-run NixOS images for headless Raspberry Pi 3 devices. Aarch64 images for NixOS need to be built natively on aarch64 hardware so the first Pi 3, the subject of this post, will need a keyboard and mouse attached for two commands.
A fair chunk of this post is collated from NixOS on ARM and NixOS on ARM/Raspberry Pi into a coherent, flowing process with additional steps related to the goal of this being a headless Raspberry Pi 3.
Head to Hydra job nixos:release-19.03:nixos.sd_image.aarch64-linux and download the latest successful build. ie:
$ wget https://hydra.nixos.org/build/95346103/download/1/nixos-sd-image-19.03.172980.d5a3e5f476b-aarch64-linux.img
You will then need to write this to your SD Card:
# dd if=nixos-sd-image-19.03.172980.d5a3e5f476b-aarch64-linux.img of=/dev/sdX status=progress
Make sure you replace "/dev/sdX" with the correct location of your SD card.
Once the SD card has been written, attach the keyboard and screen, insert the SD card into the Pi and boot it up.
When the boot process has been completed, you will be thrown to a root prompt where you need to set a password for root and start the ssh service:
[root@pi-tri:~]#
[root@pi-tri:~]# passwd
New password:
Retype new password:
passwd: password updated successfully
[root@pi-tri:~]# systemctl start sshd
You can now complete the rest of this process from the comfort of whereever you normally work.
After successfully ssh-ing in and examining your disk layout with lsblk, the first step is to remove the undersized, FAT32 /boot partition:
# fdisk -l /dev/mmcblk0
Disk /dev/mmcblk0: 7.4 GiB, 7948206080 bytes, 15523840 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x2178694e
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 * 16384 262143 245760 120M b W95 FAT32
/dev/mmcblk0p2 262144 15522439 15260296 7.3G 83 Linux
# echo -e 'a\n1\na\n2\nw' | fdisk /dev/mmcblk0
Welcome to fdisk (util-linux 2.32.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): Partition number (1,2, default 2):
The bootable flag on partition 1 is disabled now.
Command (m for help): Partition number (1,2, default 2):
The bootable flag on partition 2 is enabled now.
Command (m for help): The partition table has been altered.
Syncing disks.
# fdisk -l /dev/mmcblk0
Disk /dev/mmcblk0: 7.4 GiB, 7948206080 bytes, 15523840 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x2178694e
Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 16384 262143 245760 120M b W95 FAT32
/dev/mmcblk0p2 * 262144 15522439 15260296 7.3G 83 Linux
Next we need to configure NixOS to boot the basic system we need with ssh enabled, root and a single user and disks configured correctly. I have this example file which at the time of writing looked like this:
# This is an example of a basic NixOS configuration file for a Raspberry Pi 3.
# It's best used as your first configuration.nix file and provides ssh, root
# and user accounts as well as Pi 3 specific tweaks.
{ 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;
# For a Raspberry Pi 2 or 3):
boot.kernelPackages = pkgs.linuxPackages_latest;
# !!! Needed for the virtual console to work on the RPi 3, as the default of 16M doesn't seem to be enough.
# If X.org behaves weirdly (I only saw the cursor) then try increasing this to 256M.
boot.kernelParams = ["cma=32M"];
# File systems configuration for using the installer's partition layout
fileSystems = {
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";
fsType = "ext4";
};
};
# !!! Adding a swap file is optional, but strongly recommended!
swapDevices = [ { device = "/swapfile"; size = 1024; } ];
hardware.enableRedistributableFirmware = true; # Enable support for Pi firmware blobs
networking.hostName = "nixosPi"; # Define your hostname.
networking.wireless.enable = false; # Toggles wireless support via wpa_supplicant.
# Select internationalisation properties.
i18n = {
consoleFont = "Lat2-Terminus16";
consoleKeyMap = "us";
defaultLocale = "en_AU.UTF-8";
};
time.timeZone = "Australia/Brisbane"; # Set your preferred timezone:
# List services that you want to enable:
services.openssh.enable = true; # Enable the OpenSSH daemon.
# Configure users for your Pi:
users.mutableUsers = false; # Remove any users not defined in here
users.users.root = {
hashedPassword = "$6$eeqJLxwQzMP4l$GTUALgbCfaqR8ut9kQOOG8uXOuqhtIsIUSP.4ncVaIs5PNlxdvAvV.krfutHafrxNN7KzaM7uksr6bXP5X0Sx1";
openssh.authorizedKeys.keys = [
"ssh-ed25519 Voohu4vei4dayohm3eeHeecheifahxeetauR4geigh9eTheey3eedae4ais7pei4ruv4 me@myhost"
];
};
# Groups to add
users.groups.myusername.gid = 1000;
# Define a user account.
users.users.myusername = {
isNormalUser = true;
uid = 1000;
group = "myusername";
extraGroups = ["wheel" ];
hashedPassword = "$6$l2I7i6YqMpeviVy$u84FSHGvZlDCfR8qfrgaP.n7/hkfGpuiSaOY3ziamwXXHkccrOr8Md4V5G2M1KcMJQmX5qP7KOryGAxAtc5T60";
openssh.authorizedKeys.keys = [
"ssh-ed25519 Voohu4vei4dayohm3eeHeecheifahxeetauR4geigh9eTheey3eedae4ais7pei4ruv4 me@myhost"
];
};
# This value determines the NixOS release with which your system is to be
# compatible, in order to avoid breaking some software such as database
# servers. You should change this only after NixOS release notes say you
# should.
system.stateVersion = "19.03"; # Did you read the comment?
system.autoUpgrade.enable = true;
system.autoUpgrade.channel = https://nixos.org/channels/nixos-19.03;
}
Once this is copied into place, you only need to rebuild NixOS using it by running:
# nixos-rebuild switch
Now you should have headless Pi 3 which you can use to build CD card images for other Pi 3's that are fully configured and ready to run.