Installing NixOS on Raspberry Pi 4

Nix is a tool that allows you to define your software environment from code. Nix has several components to it, and one of the most interesting to me is NixOS, which lets you use Nix tooling to define your entire OS configuration using plaintext files.

I only recently started experimenting with Nix, and there’s a huge amount to learn. One of the first things I tried to do was install NixOS on my Raspberry Pi, but my first several attempts failed. Every NixOS Pi tutorial I could find was either incomplete or out of date.

I present to you my complete and working guide to installing NixOS on a Raspberry Pi 4. I’m a newcomer to NixOS, so this guide is for Nix beginners, but I assume you have basic familiarity with Raspberry Pi and Linux.

Requirements ๐Ÿ”—︎

To follow this tutorial, you’ll need:

  • A Raspberry Pi 4
  • A microSD card with at least 8 GB of storage
  • A microSD writer
  • A separate computer to flash the microSD card

Download the NixOS microSD image ๐Ÿ”—︎

To begin, download the NixOS microSD image from the link below:

Decompress the NixOS microSD image ๐Ÿ”—︎

The NixOS team compresses their microSD images with an uncommon compression format called Zstandard, an open-source format from Facebook.

To decompress the NixOS image, download the latest Zstandard release for your platform:

Once you have both the Zstandard tool and the NixOS microSD image, decompress the .img.zst file with the following command:

zstd --decompress "nixos-sd-image-23.11pre515819.8ecc900b2f69-aarch64-linux.img.zst"

Decompressing the Zstandard file should produce a file called nixos-sd-image-23.11pre515819.8ecc900b2f69-aarch64-linux.img.

Flash the NixOS microSD image ๐Ÿ”—︎

After you’ve decompressed the image, flash it to a microSD using your favorite microSD flashing utility.

If you don’t know which microSD flashing tool to use, I recommend balenaEtcher. It’s user-friendly and works on every major OS.

Screenshot of balenaEtcher

When you flash the microSD, choose the .img file rather than the .img.zst file, as most flashing tools won’t understand the Zstandard format.

Insert the microSD card into your Pi ๐Ÿ”—︎

After you flash the microSD, insert it into the microSD slot of your Raspberry Pi:

Photo of microSD inserted into microSD slot of Raspberry Pi

Insert the flashed microSD card into your Pi’s microSD slot.

Connect a display and keyboard to your Pi ๐Ÿ”—︎

Most Raspberry Pi images offer a way to access the device over the network on the first boot. I haven’t found a way to do that with NixOS, so you’ll need to temporarily connect a keyboard and HDMI display to your Pi to see what’s happening.

Photo of HDMI display and keyboard connected to a Raspberry Pi as it boots NixOS

NixOS has no fully-networked install, so you’ll need to connect a keyboard and HDMI display during the initial setup.

For this tutorial, I’m controlling my Pi with TinyPilot, a device I created for situations just like this.

You don’t need a TinyPilot for this tutorial, as you can follow along with a plain old keyboard and HDMI display.

Boot your NixOS system ๐Ÿ”—︎

It’s time for the moment of truth. Power on your Raspberry Pi.

If everything went well, you should see a boot sequence like the following:

A successful boot of the NixOS microSD image on a Raspberry Pi 4.

The boot is complete when you see the NixOS command prompt:

[nixos@nixos~:]$

If the boot failed, try updating your Pi’s bootloader to the latest available version and then trying again.

Enable SSH access (optional) ๐Ÿ”—︎

When working with Raspberry Pis, I find SSH much more convenient than typing on a separate keyboard.

There are two options for enabling SSH access on a fresh NixOS system.

Option 1: Add a password ๐Ÿ”—︎

On the NixOS system, you can assign a password to the default nixos user account by running the following command:

passwd

Once you’ve set a password, you can SSH into your NixOS system normally:

ssh nixos@nixos.local

Option 2: Add an SSH key ๐Ÿ”—︎

You can also add your SSH public key as an authorized key on the system.

If you authenticate to Github with SSH keys, Github offers a convenient way to download your public SSH key to any device:

GITHUB_USERNAME="your-github-username" # Replace this.

mkdir -p ~/.ssh && \
  curl "https://github.com/${GITHUB_USERNAME}.keys" > ~/.ssh/authorized_keys

If you see an error that says certificate is not valid yet, it means that your Pi is still synchronizing its system time. Wait 60 seconds, and try the command again.

Once you’ve added your public SSH key to the NixOS system, you can SSH in like normal:

ssh nixos@nixos.local

Write the NixOS configuration file ๐Ÿ”—︎

You’re now in NixOS!

There’s not much you can do yet because it’s a minimal NixOS environment with nothing installed.

To make your NixOS experience more interesting, install a desktop GUI and a few applications. To begin, download my example NixOS configuration file:

curl \
  --show-error \
  --fail \
  https://mtlynch.io/nixos-pi4/configuration.nix \
  | sudo tee /etc/nixos/configuration.nix

You can make changes to /etc/nixos/configuration.nix at this point using nano or vim. You might want to change the hostname, user, or password values at the top.

sudo nano /etc/nixos/configuration.nix

Don’t worry too much about perfecting the configuration file just yet. With NixOS, you can change your mind about any option at any time, and applying the change is as easy as editing the configuration file again.

When you’re happy with your configuration.nix file, run these commands to apply the configuration to your system and reboot:

sudo nixos-rebuild boot && \
  echo "install complete, rebooting..." && \
  sudo poweroff --reboot

When the reboot completes, you should see a screen that looks like this:

Your Pi is now running NixOS with a Gnome desktop environment!

If you used the default configuration.nix file above, your username is tempuser and your password is somepass.

Experimenting with NixOS ๐Ÿ”—︎

At this point, your NixOS system is up and running.

You’re free to explore NixOS as you wish, but I’ve included a couple of beginner experiments you can try on your new system.

Experiment 1: Change the desktop enviroment ๐Ÿ”—︎

The configuration.nix file above assumes that you want to use the Gnome desktop environment, but maybe you prefer a different one. There’s another desktop manager called Plasma that’s similar in design to Microsoft Windows.

To change your NixOS system to use Plasma instead of Gnome, open the your configuration.nix file in a text editor:

sudo nano /etc/nixos/configuration.nix

Find these lines in the file:

    displayManager.gdm.enable = true;
    desktopManager.gnome.enable = true;

Replace them with these lines:

    displayManager.sddm.enable = true;
    desktopManager.plasma5.enable = true;

To apply the changes save the file, exit nano, and run these commands:

sudo nixos-rebuild boot && sudo reboot

When you reboot, you should see a desktop like the following:

All it took to change your whole desktop environment was just a two-line change.

Experiment 2: Create an ad-hoc software environment ๐Ÿ”—︎

One of the most approachable Nix tools I’ve found is nix-shell. It lets you create software environments on the fly with any software packages you specify.

nix-shell doesn’t affect any other configuration on your system, so you’re free to try new tools without the risk of breaking anything else.

I sometimes run into projects I wrote a few years ago that depend on an older version of Node.js. I’ve tried tools like nvm to install Node versions side-by-side, but I always end up spending 20 minutes remembering how to use nvm and configure it correctly.

Even though nix-shell is a general purpose tool for installing packages, I find it more convenient even than language-specific dev tools like nvm.

Here’s how you can create a nix-shell environment with Node.js 18.x:

$ nix-shell --packages nodejs-18_x
these paths will be fetched (11.25 MiB download, 52.36 MiB unpacked):
  /nix/store/87kgx3ym4kgmqwaijckqvbfrkzm8ax75-nodejs-18.2.0
copying path '/nix/store/87kgx3ym4kgmqwaijckqvbfrkzm8ax75-nodejs-18.2.0' from 'https://cache.nixos.org'...

[nix-shell:~]$ node --version
v18.2.0

[nix-shell:~]$ npm --version
8.9.0

When you’re done with the environment, just hit Ctrl+D or type exit.

Here’s how you can do the same thing to create a Node.js 16.x environment:

$ nix-shell --packages nodejs-16_x
these paths will be fetched (10.77 MiB download, 50.24 MiB unpacked):
  /nix/store/1ba3sqw3rkadg2ksywqc85lq2hvx9fvk-nodejs-16.15.0
copying path '/nix/store/1ba3sqw3rkadg2ksywqc85lq2hvx9fvk-nodejs-16.15.0' from 'https://cache.nixos.org'...

[nix-shell:~]$ node --version
v16.15.0

[nix-shell:~]$ npm --version
8.5.5

Troubleshooting ๐Ÿ”—︎

Upgrade to the latest Pi bootloader ๐Ÿ”—︎

If you’re running into boot issues with NixOS, you may need to update your Pi’s bootloader and EEPROM.

Boot a recent build of Raspberry Pi OS (aka “Raspbian”), then run these command to install the latest bootloader:

sudo raspi-config nonint do_boot_rom E1 && \
  sudo reboot

To update the EEPROM, run these commands:

sudo apt update && \
  sudo apt install --yes rpi-eeprom && \
  sudo rpi-eeprom-update -a && \
  sudo reboot

The Pi 4 devices I tested booted the NixOS 23.11 disk image out of the box, so the above steps weren’t necessary for me.

Appendix: Failed attempts ๐Ÿ”—︎

In creating this tutorial, I ran into a ton of paths that didn’t work. I’ve collected them here for the sake of saving others time retrying the same steps.


Thanks to Alex Groleau from the NixOS documentation team for his help with this guide and his work on the official NixOS Raspberry Pi tutorial.