Skip to content

Commit

Permalink
Let's make this into a tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
zupo committed Dec 26, 2018
1 parent 76d5637 commit 0ffbf6a
Show file tree
Hide file tree
Showing 8 changed files with 1,499 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
smb-secrets
README.html
157 changes: 157 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Getting started with NixOS on Raspberry Pi 3 Model B+

This is a step-by-step guide on diving into NixOS by installing and using it on a Raspberry Pi 3 Model B+.

The official documentation on running [NixOS on ARM](https://nixos.wiki/wiki/NixOS_on_ARM), and more specifically, [on a Raspberry Pi](https://nixos.wiki/wiki/NixOS_on_ARM/Raspberry_Pi) is quite good. But it has to cover a lot of edge cases and old revisions, plus it assumes some prior experience with NixOS. I had none. I did however have the luxury of [@domenkozar](https://github.com/domenkozar) hand-holding me through instructions so that I could build my first NixOS install. But not everyone has the same privilege! And for those of you, this guide tries to provide enough hand-holding so that anyone with some *general* Linux experience is able to follow.

To make the guide even easier to follow, it is focused only on a single board: the Raspberry Pi 3 Model B+.

The end game is a robust and future-proof Media Center running on a Raspberry Pi 3.

## Assumptions

* The development machine that you will use to work through this guide, is a MacBook or a Linux machine you know well (and you can Google Linux alternatives for MacOS apps)
* The Raspberry Pi will use wired ethernet, and will get the IP via DHCP.
* The Raspberry Pi is connected to an HDMI display.
* You have a somewhat decent Internet connection.

## Minimal install

First, we'll do a minimal install of NixOS on your Raspberry Pi. Adding extra software on top of the minimal install is very easy and we'll do it later.

### Preparing the SD card

1. Download `sd-image-aarch64-linux.img` from https://www.cs.helsinki.fi/u/tmtynkky/nixos-arm/installer/.
2. Insert the SD card into the MacBook.
3. Unmount (not eject) the SD card partition using the Disk Utility app.
4. Open up the Terminal app and run `diskutil list` and note down the device number of the SD card.
5. Run `sudo dd bs=1m if=sd-image-aarch64-linux.img of=/dev/rdiskN conv=sync`. Replace `N` with the SD card device number.
6. Monitor progress with `Ctrl+T`.

A better guide for the steps above is in the [official Raspberry Pi documentation](https://www.raspberrypi.org/documentation/installation/installing-images/).

### First boot

1. Put the SD card into the Pi and turn it on. If all goes well, you should be dropped into a root shell.
2. I like to get SSH running ASAP, so that I can use my MacBook's keyboard and terminal emulator to copy over
commands. I'm a creature of comfort:

```bash
[root@nixos:~]# passwd # to set root password to be able to SSH into the Raspberry Pi
[root@nixos:~]# systemctl start sshd # start SSH service
[root@nixos:~]# ifconfig # to see which IP was assigned to the Raspberry Pi
```

3. Now that we can SSH into the Pi, we can start with some RealWork™. Let's tell nix to use a stable release of NixOS.

```bash
~ $ ssh root@<Pi's IP>
[root@nixos:~]# nix-channel --list

nixos https://nixos.org/channels/nixos-unstable
# The pre-built ARM image that we downloaded and flashed onto the SD card
# is configured to use the bleeding edge NixOS. We don't want that. We're
# building a Media Center and we care about stability and future proofing.
# Let's use the latest *stable* NixOS release.

[root@nixos:~]# nix-channel --remove nixos
[root@nixos:~]# nix-channel --add https://nixos.org/channels/nixos-18.09

[root@nixos:~]# nix-channel --list
nixos https://nixos.org/channels/nixos-18.09

[root@nixos:~]# nix-channel --update
# This will take some time, depending on your Internet connection.
```

4. Copy over the contents of [`minimal.nix`](https://github.com/zupo/nix/blob/master/minimal.nix) into `/etc/nixos/configuration.nix`.

```bash
[root@nixos:~]# nano /etc/nixos/configuration.nix
{ config, pkgs, lib, ... }:
{
# NixOS wants to enable GRUB by default
boot.loader.grub.enable = false;
# if you have a Raspberry Pi 2 or 3, pick this:
boot.kernelPackages = pkgs.linuxPackages_latest;
...
```

5. And we're ready to build our minimal configuration.
```bash
[root@nixos:~]# nixos-rebuild switch
building Nix...
building the system configuration...
these derivations will be built:
...
```
Building will take some time and generate [lots of output](https://github.com/zupo/nix/blob/master/minimal.output). For me it took a little over an hour. Remember we told nix we want to use the latest stable release of NixOS, instead of the bleeding edge, so the entire distribution needs to be downloaded, built and configured.
6. Reboot.
### Cleanup
At this point, your Raspberry Pi should boot into the minimal NixOS configuration defined in [`minimal.nix`](https://github.com/zupo/nix/blob/master/minimal.nix). Let's do some cleanup before we continue.

```bash
~ $ ssh root@<Pi's IP>
[root@nixos:~]# nix-collect-garbage -d # remove old pre-built configuration and all of its dependencies
[root@nixos:~]# nixos-rebuild switch # remove old boot entries
[root@nixos:~]# reboot
```
# Ready for features
And that's basically it! You have a minimal NixOS running on your Raspberry PI. Now browse `.nix` files in the [`features/` directory](https://github.com/zupo/nix/features), copy their configuration into your `/etc/nixos/configuration.nix` and re-run `nixos-rebuild switch`.
Let's try one for practice: the home theater software Kodi.
1. Add the following to `/etc/nixos/configuration.nix`:
```bash
# Enable X11 windowing system
services.xserver.enable = true;
services.xserver.videoDrivers = [ "modesetting" ];
# Enable Kodi
services.xserver.desktopManager.kodi.enable = true;
# Enable slim autologin
services.xserver.displayManager.lightdm.enable = true;
services.xserver.displayManager.lightdm.autoLogin.enable = true;
services.xserver.displayManager.lightdm.autoLogin.user = "kodi";
# Define a user account
users.extraUsers.kodi.isNormalUser = true;
```
2. Run `nixos-rebuild switch` and `reboot` when it's done.
## Thanks
* [@domenkozar](https://github.com/domenkozar) for not shying away from my endless harassment on IM when I got stuck. Which was often. It truly is a testament to NixOS' design that Domen was able to debug and fix my problems from 2500 kilometers away. And that's only because all "system state" is in the `configuration.nix` file and not sprinkled among tens of opaque locations around the filesystem.
* [@dezdeg](https://github.com/dezgeg) for providing pre-built images that boot on the Raspberry Pi. No chance I'd be able to do this myself.
* Other folks on the #nixos-aarch64 IRC channel for support and insights.
## TODO
* Update `configuration.nix` to use minimal.nix and features/.
* Figure out how to elegantly add a swap partition to the SD card
```
# !!! Adding a swap file is optional, but strongly recommended!
# swapDevices = [ { device = "/swapfile"; size = 1024; } ];
```
* Pin the version of NixOS we are using, so we truly get a deterministic and future-proof build.
* Is this still true? How can I test it?
```
* Note: The mainline kernel (tested with nixos kernel 4.18.7) does not include support for cpu frequency scaling on the Raspberry Pi. To get higher clock speed, set force_turbo=1 in /boot/config.txt
```
7 changes: 2 additions & 5 deletions configuration.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
{
# 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;

# if you have a Raspberry Pi 2 or 3, pick this:
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
boot.kernelParams = ["cma=256M"];

hardware.firmware = [ pkgs.raspberrypifw ];
boot.loader.raspberryPi.enable = true;
boot.loader.raspberryPi.version = 3;
boot.loader.raspberryPi.uboot.enable = true;
Expand All @@ -33,7 +30,7 @@

in ["${automount_opts},credentials=/etc/nixos/smb-secrets"];
};

networking = {
hostName = "tv";
interfaces.eth0.ipv4.addresses = [{
Expand All @@ -54,7 +51,7 @@
passwordAuthentication = false;
};

# Enable the X11 windowing system
# Enable X11 windowing system
services.xserver.enable = true;
services.xserver.videoDrivers = [ "modesetting" ];

Expand Down
154 changes: 154 additions & 0 deletions features/common.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# A bunch of things I expect to have on every machine I work with.

{ config, pkgs, lib, ... }:
{

# Niceties
programs.bash.enableCompletion = true;
environment.variables.EDITOR = "vim";
time.timeZone = "UTC";

# My usual account
users.users.zupo = {
isNormalUser = true;
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC3mRrO/NGw6q9wysAFmtqw8jKy7U4R5o3Hb/CnbAKnuJAlAOJX4gXzkXEHJF++6sm4cEe8GPvdfWwgTM8ysV53qQCVlmCCbcYvel4WkpOAJqsWljuwCtoDpYRoGhM4EvPe4oFmZ7sHKKFqCA03eIohG32wXmHvc3BQCD6YbvT8r6gg39XDiM3cwxsro6y8qCeo4I+qnPtt3bksMU2QgzCj3G3tytW2ZhYmEQL9Cu8PGxXVNXvBtBwLJtUq6MV5aoF/DKImD7zc7sYA+kRH+NXtFtrD3IdW3/eTpOIZTN95cyx4sAkx4zF8Pxefww4YugH+cYo9ZFFlxidnHThQSJRZL0DwAdVK7rpNxW5538snAZclQx6F9PdmesJ60Kqn+h92OnoEFSjuY0Tct2qdXou10gJmspCu0VnFIntDOxIAGrvNFC3mHYLhhwf84YnoGOt0yaIF9K9ewevxcxjnKp9BXTEcMxvPCXieoIhF01hp9GssmcRNLqNyqNNgi1pgLDSkaFetq+liw15jsK6t4vYHfjMqg9ynZ9P/wrVl8hUZ0KK+DLyrUdAFO4oUxRwFyz1uv71RoYV7MDWi+XdxycR4cGdBwg//BV5+zPOIf+4gpVT6TQPi0svymYbBNLoQRbMEQHXiTmgPdQ09npIIQ9xyClP1KE1CpaFkCJ/lQWtmrQ==" ];
};

# NTP clock synchronization
services.timesyncd.enable = true;

# Harden SSH
programs.ssh.startAgent = true;
services.openssh = {
enable = true;
permitRootLogin = "no";
passwordAuthentication = false;
};

# Filter incoming traffic
networking.firewall.enable = true;
networking.firewall.allowPing = true;
networking.firewall.allowedTCPPorts = [ 22 ];
networking.firewall.allowedUDPPorts = [ ];

# Install things I'm used to having around
environment.systemPackages = with pkgs; [
(import ./vim.nix)
git
wget
curl
pwgen
links2
ncdu
iftop
iotop
rsync
screen
telnet
raspberrypi-tools
];

environment.etc."gitconfig".text = ''
[user]
name = Nejc Zupan
email = ${ builtins.readFile ./secrets/email }
[core]
editor = vim
excludesfile = /etc/gitignore
# Highlight whitespace errors in git diff:
whitespace = tabwidth=2,tab-in-indent,cr-at-eol,trailing-space
[color]
diff = auto
status = auto
branch = auto
ui = auto
[alias]
ap = add -p
cdiff = diff --cached
sdiff = diff --staged
st = status
ci = commit
cia = commit -v -a
cp = cherry-pick
br = branch
co = checkout
df = diff
dfs = diff --staged
l = log
ll = log -p
rehab = reset origin/master --hard
pom = push origin master
phm = push heroku master
latest = for-each-ref --sort=-committerdate refs/heads/
[push]
default = simple
[branch]
autosetuprebase = always
[help]
autocorrect = 1
[credential]
helper = cache --timeout=3600
'';

environment.etc."gitignore".text = ''
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
*.lo
*.la
*.rej
*.pyc
*.pyo

# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
#*.zip

# Logs and databases #
######################
*.log
*.sql
*.sqlite

# OS generated files #
######################
.DS_Store
.DS_Store?
ehthumbs.db
Icon?
Thumbs.db

# Python projects related #
###########################
*.egg-info
.egg-info.installed.cfg
*.pt.py
*.cpt.py
*.zpt.py
*.html.py
*.egg
*.Python
}
File renamed without changes.
8 changes: 4 additions & 4 deletions minimal.nix
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
};
};

# !!! Adding a swap file is optional, but strongly recommended!
# swapDevices = [ { device = "/swapfile"; size = 1024; } ];

# Preserve space and make builds faster by sacrificing documentation
# Preserve space by sacrificing documentation and history
services.nixosManual.enable = false;
nix.gc.automatic = true;
nix.gc.options = "--delete-older-than 30d";
boot.cleanTmpDir = true;

# Configure basic SSH access
services.openssh.enable = true;
Expand Down
Loading

0 comments on commit 0ffbf6a

Please sign in to comment.