Installing NixOS (headless) on a Rasberry Pi 3

contents

Installing NixOS on a Raspberry Pi, why? In my opinion, the experience of running NixOS on Raspberry Pi is pretty much flawless, and it's nice to have a deterministic, minimal, and easily (re)deployable OS image for your RPi, to make it do whatever it is you want it to do, from a local home server, to an embedded controller in a larger project.

Currently the Raspberry Pi version 3 is the only Pi with first class NixOS support, the previous Pis being ARMv6 and ARMv7 rather than AArch64/ARMv8. It seems it's possible to run NixOS on a Pi 1 or 2, but the path of least resistance is certainly using a Pi 3. It seems support for version 4 will arrive at some point.

Requirements

Useful resources

Download NixOS image

You can download the NixOS image from Hydra, as stated here, for:

Download the image for the latest successful build.

Flash to SD

Untar the image and flash the SD card from your host system as follows.

Ensure device partitions are unmounted:

sudo umount /dev/mmcblk0*

Flash without monitoring progress:

bzip2 -cd nixos-sd-image-*-aarch64-linux.img.bz2 | sudo dd of=/dev/mmcblk0 bs=4M

Or flash while monitoring progress with pv:

bzip2 -cd nixos-sd-image-*-aarch64-linux.img.bz2 | pv | sudo dd of=/dev/mmcblk0 bs=4M

Unmount:

sync
sudo umount /dev/mmcblk0*

For me, the SD block device path was /dev/mmcblk0, the image has to be written to the whole device, rather than a partition, as the image contains a complete disc image including partition table.

The image written to the SD card resizes itself automatically on first boot.

Enabling SSH (optional)

On boot, the system has no user passwords, and SSH is disabled. For this reason, you will need to use a USB keyboard to set passwords using passwd, and enable SSH if you require:

sudo systemctl start sshd

If you start SSH in this way, it will only be temporary, however, and will not persist after reboot, until you add a nix configuration as shown below.

Configuration

The following takes inspiration from:

I have removed GPU memory since this is to be used as a headless server, rather than running any graphical environment.

{ config, pkgs, lib, ... }:
{
  boot.loader.grub.enable = false;

  boot.kernelPackages = pkgs.linuxPackages_latest;
  #boot.kernelParams = ["cma=256M"];

  boot.loader.generic-extlinux-compatible.enable = true;
  boot.loader.raspberryPi.uboot.enable = true;
  boot.loader.raspberryPi.enable = true;
  boot.loader.raspberryPi.version = 3;

  # System packages
  environment.systemPackages = with pkgs; [
    vim
  ];

  fileSystems = {
    "/" = {
      device = "/dev/disk/by-label/NIXOS_SD";
      fsType = "ext4";
    };
  };

  # Preserve space by disabling documentation and enaudo ling
  # automatic garbage collection
  documentation.nixos.enable = false;
  nix.gc.automatic = true;
  nix.gc.options = "--delete-older-than 30d";
  boot.cleanTmpDir = true;

  # Configure SSH
  services.openssh.enable = true;
  services.openssh.permitRootLogin = "yes";

  # Add users
  users.users = {
    root = {};
    pi = {
      isNormalUser = true;
      home = "/home/nixos";
      extraGroups = [ "wheel" ];
      hashedPassword = "$6$JYcd0P.HQIE3ayRp$yNZYogMy99HUzmip3MAea78V9dMMoN7yXroOpkmOmLxvSjXpXpNNfOZF0VptJ7h5wvP5uZ.ASuzUkKwEhIJAJ1";
      # consider adding a hashed password, or ssh key here, see:
      # https://eipi.xyz/blog/installing-nixos-on-a-rasberry-pi-3#?
    };
  };

  swapDevices = [ { device = "/swapfile"; size = 1024; } ];
}

The nixos user's password will be qqcTch4m.

If you want to change the user's password, change the string in hashedPassword to one generated by:

mkpasswd -m sha-512

In some distros, this tool can be found in the whois package.

If you want to use the user nixos and give it a password, so it's possible to log in and the user is able to use sudo, you may have some difficulties, I did.

Configure and nixos-rebuild

The easiest way to proceed is to attach a USB keyboard and an HDMI monitor to the Pi, since even if you use SSH, you will need a keyboard and monitor to start sshd.

We need some way to apply an /etc/nixos/configuration.nix to the SD card. I used a USB flash device, copying the configuration below onto it, and mounting and copying off the memory stick onto the RPi as follows:

mkdir /tmp/mnt
sudo mount /dev/sda1 /tmp/mnt
sudo cp /tmp/mnt/configuration.nix /etc/nixos/

And then rebuild:

sudo nixos-rebuild switch

If you want to log the rebuild log for any reason, you could use the already mounted flash stick:

sudo nixos-rebuild switch | sudo tee /tmp/mnt/rebuild-log

You don't need to set a password before doing this, as the nixos user present by default seems to be in the wheel group, so has sudo.

You need internet connectivity to be able to rebuild NixOS, for which the easiest way is likely to connect it to Ethernet, test with ping 1.1.1.1.

Upgrade

Once up and running, you may want to add a NixOS channel and upgrade:

nix-channel --add https://nixos.org/channels/nixos-unstable nixos
nixos-rebuild switch --upgrade

Possible configuration additions

Static IP

If you want to give the Pi a static IP, add the following to the configuration.

{

  networking = {
    usePredictableInterfaceNames = false;
    interfaces.eth0.ipv4.addresses = [{
      address = "192.168.1.160";
      prefixLength = 24;
    }];
    defaultGateway = "192.168.1.1";
    nameservers = [
      "192.168.1.1"
      "1.1.1.1"
      "8.8.8.8"
    ];
  };

}