blog: nixos-pinephone: tune & validate the guide

This commit is contained in:
colin 2022-06-15 16:03:00 -07:00
parent f3f5a87d76
commit 349fd69db7
1 changed files with 148 additions and 136 deletions

View File

@ -5,85 +5,64 @@ description = "a flake-based quickstart and some tuning"
extra.hidden = true
+++
there is no official, easy-to-grab NixOS image to download and flash to devices like the pinephone today. although there is [a way](https://news.ycombinator.com/item?id=30010178) to do that via the hydra build cache, it's a bit tortured and since the images built in automation don't have a user, you'll have to manually edit `/etc/fstab` (from a different machine) to add a user and then login with a USB-C keyboard to setup ssh or a desktop.
there is no official, easy-to-grab NixOS image to download and flash to devices like the pinephone today. although there is [a way](https://news.ycombinator.com/item?id=30010178) to do that via the hydra build cache, it's a bit tortured and the images built in automation don't have a user. the bootstrapping process would require manually editing `/etc/fstab` (from a different machine) to add a user and then logging in with a USB-C keyboard to setup ssh or a desktop -- neither user-friendly nor easily reproducible.
so let's bootstrap from an existing Nix install. first, provision a machine. the architecture doesn't matter -- i'll assume it's x86_64 and show you how to cross-compile. i recommend using a flake for this. i'm basing this around nix-22.05, so something like this:
```flake.nix
so let's bootstrap from an existing Nix install. first, provision a machine (either a NixOS installation or something with the `nix` binary installed). the architecture doesn't matter -- i'll assume it's x86_64 and show you how to cross-compile. create a new directory for the work
```sh
mkdir nixos-pinephone-getting-started && cd nixos-pinephone-getting-started
```
each section in this article corresponds to one commit in [this](https://git.uninsane.org/colin/nixos-pinephone-getting-started)repository in case you want to skip the commentary.
## Bare-minimal Build
for our Pinephone machine, we'll let [mobile-nixos](https://github.com/NixOS/mobile-nixos/) do the heavy lifting. create a minimal `flake.nix`:
```nix
{
inputs = {
nixpkgs.url = "nixpkgs/nixos-22.05";
# nixpkgs.url = "nixpkgs/nixos-22.05";
nixpkgs.url = "nixpkgs/dfd82985c273aac6eced03625f454b334daae2e8";
mobile-nixos = {
# url = "github:nixos/mobile-nixos";
url = "github:nixos/mobile-nixos/efbe2c3c5409c868309ae0770852638e623690b5";
flake = false;
};
};
outputs = { self, nixpkgs }: {
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
outputs = { self, nixpkgs, mobile-nixos }: {
pinephone-img = (nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
modules = [
./host.nix
(import "${mobile-nixos}/lib/configuration.nix" {
device = "pine64-pinephone";
})
({ ... }: {
nixpkgs.config.allowUnfree = true;
})
];
};
}).config.mobile.outputs.u-boot.disk-image;
};
}
```
`host.nix` is where the configuration of your host image lives. if you installed nix through the installer, you can just copy the contents from `/etc/nixos/configuration.nix` into it.
## Bare-minimal Build
for our Pinephone machine, we'll let [mobile-nixos](https://github.com/NixOS/mobile-nixos/) do the heavy lifting. add it as an input to `flake.nix`:
```diff -u a/flake.nix b/flake.nix
inputs = {
nixpkgs.url = "nixpkgs/nixos-22.05";
+ mobile-nixos = {
+ url = "github:nixos/mobile-nixos";
+ flake = false;
+ };
};
```
and then add a new target for building a `pinephone-img`:
```diff -u a/flake.nix b/flake.nix
- outputs = { self, nixpkgs }: {
+ outputs = { self, nixpkgs, mobile-nixos }: {
+ pinephone-img = (nixpkgs.lib.nixosSystem {
+ system = "aarch64-linux";
+ modules = [
+ (import "${mobile-nixos}/lib/configuration.nix" {
+ device = "pine64-pinephone";
+ })
+ ];
+ }).config.mobile.outputs.u-boot.disk-image;
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
...
```
i've frozen the mobile-nixos commit here because they're in the process of switching from U-Boot to towboot -- the pinned commit is among the last which support U-Boot, which this guide assumes. pinning `nixpkgs` is hopefully paranoia: try switching to `nixpkgs/nixos-<release>` after you're bootstrapped.
if your host machine isn't aarch64, you'll have to enable cross compiling. the trivial way is emulation (i.e. qemu), though it could make the build take a full afternoon depending on how much of your system is available through nixcache.
```diff -u a/flake.nix b/flake.nix
outputs = { self, nixpkgs, mobile-nixos }: {
...
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./host.nix
+ { ... }: {
+ boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
+ }
];
};
};
on the host box, add this somewhere in your config (e.g. `/etc/nixos/configuration.nix`):
```nix
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
```
if you added this emulation, rebuild your host machine to enable it (`nixos-rebuild --flake './#host' switch).
if you added this emulation, rebuild your host machine to enable it (`nixos-rebuild --flake /etc/nixos switch).
then build the image with:
```sh
nix build './#pinephone-img'
nixos-pinephone-getting-started$ nix build './#pinephone-img'
```
the resulting image is effectively the same as what Hydra spits out in the automation: no users, and no way to login. if you're brand new to the Pinephone, i recommend flashing the image and booting as a sanity check. otherwise, skip to _Making the Image More Usable_ where we'll build a more usable image. at any time, consult the _Troubleshooting_ section if you hit something unexpected.
the resulting image is effectively the same as what Hydra spits out in the automation: no users, and no way to login. if you're brand new to the Pinephone, i recommend flashing the image and booting as a sanity check. otherwise, skip to [_Making the Image More Usable_](#making-the-image-more-usable) make this a markdown section link) where we'll build a more usable image. at any time, consult the _Troubleshooting_ section if you hit something unexpected.
### Flashing the Image
you might be tempted to flash this to the eMMC instead of an SD card, thinking that the former will be more reliable storage. it's not: my eMMC began to fail within 20 write cycles. i highly recommend you use an SD card for this.
@ -94,7 +73,7 @@ sudo dd if=$(readlink ./result)$ of=/dev/sdb bs=4M oflag=direct conv=sync status
`oflag=direct` makes the progress bar actually usable -- otherwise it'll just show how much data has been passed to the kernel -- and `conv=sync` seems to deal better with low quality SD cards (it feels like paranoia, but if i `fsck -f` the result it sometimes shows corruption without this flag).
insert the card to the Pinephone and boot it. the SD slot takes precedent over the eMMC in its boot sequence, so nothing more is needed. you should see the power indicator turn red (indicating that the builtin bootloader is active), then yellow (u-boot), then green (NixOS stage 1) (TODO: verify this). you'll see a mobile-NixOS splash screen, it'll take a minute to validate the fs, and then boot into stage 2 where you'll be stuck at a login prompt.
insert the card to the Pinephone and boot it. the SD slot takes precedent over the eMMC in its boot sequence, so nothing more is needed. you should see the power indicator turn red (indicating that U-Boot is active), then yellow (u-boot has run the mobile-nixos boot script), then green (NixOS stage 1). you'll see a mobile-NixOS splash screen, it'll take a minute to validate the fs, and then boot into stage 2 where you'll be stuck at a login prompt.
## Making the Image More Usable
we'll want to rebuild the image and include a user, desktop environment, and some basic applications. i tried Phosh, Plasma Mobile, and Gnome: of these, Phosh works the best OOTB _by far_ (Plasma Mobile is _slow_ and crash-prone, Gnome lacks an on-screen keyboard outside the core apps and its navigation is less tailored to phones).
@ -103,9 +82,9 @@ we'll use home-manager to make user setup a bit nicer. add that to `flake.nix`:
```diff -u a/flake.nix b/flake.nix
inputs = {
nixpkgs.url = "nixpkgs/nixos-22.05";
nixpkgs.url = "nixpkgs/dfd82985c273aac6eced03625f454b334daae2e8";
mobile-nixos = {
url = "github:nixos/mobile-nixos";
url = "github:nixos/mobile-nixos/efbe2c3c5409c868309ae0770852638e623690b5";
flake = false;
};
+ home-manager.url = "github:nix-community/home-manager/release-22.05";
@ -116,29 +95,113 @@ add a module for this pinephone build:
```diff -u a/flake.nix b/flake.nix
- outputs = { self, nixpkgs, mobile-nixos }: {
+ outputs = { self, nixpkgs, mobile-nixos, home-manager }: {
pinephone-img = (pkgs-mobile.lib.nixosSystem {
system = "aarch64-linux";
modules = [
(import "${mobile-nixos}/lib/configuration.nix" {
device = "pine64-pinephone";
})
+ ./pinephone.nix
pinephone-img = (pkgs-mobile.lib.nixosSystem {
system = "aarch64-linux";
+ specialArgs = { inherit home-manager; };
modules = [
(import "${mobile-nixos}/lib/configuration.nix" {
device = "pine64-pinephone";
})
- ({ ... }: {
- nixpkgs.config.allowUnfree = true;
- })
+ ./modules/default.nix
];
}).config.mobile.outputs.u-boot.disk-image;
...
```
and populate `pinephone.nix` as such:
and then populate `modules/default.nix`. you could just throw everything in here, but it's more readable (and reusable -- in case you want to share this config across machines) if you split it up:
```pinephone.nix
{ home-manager, pkgs }:
```nix
{ ... }:
{
system.stateVersion = "21.11";
imports = [
./hardware.nix
./home-manager.nix
./phosh.nix
./users.nix
];
system.stateVersion = "22.05";
nixpkgs.config.allowUnfree = true;
}
```
`modules/hardware.nix`:
```nix
{ ... }:
{
## enable the hardware rotation sensor
hardware.sensor.iio.enable = true;
## configure a user:
hardware.opengl.enable = true;
hardware.opengl.driSupport = true;
}
```
`modules/home-manager.nix`:
```nix
{ pkgs, home-manager, ... }:
{
imports = [
home-manager.nixosModule
];
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.colin = {
home.stateVersion = "21.11";
home.username = "colin";
home.homeDirectory = "/home/colin";
programs = {
home-manager.enable = true;
firefox.enable = true;
git.enable = true;
};
# a few useful packages to start with
home.packages = with pkgs; [
# useful CLI/admin tools to have during setup
fatresize
gptfdisk
networkmanager
sudo
vim
wget
# it's good to have a variety of terminals (x11, Qt, GTK) to handle more failures
xterm
plasma5Packages.konsole
gnome.gnome-terminal
];
};
}
```
`modules/phosh.nix`:
```
{ ... }:
{
services.xserver.desktopManager.phosh = {
enable = true;
user = "colin";
group = "users";
};
environment.variables = {
# Qt apps won't always start unless this env var is set
QT_QPA_PLATFORM = "wayland";
};
}
```
`modules/users.nix`:
```nix
{ ... }:
{
users.mutableUsers = false;
users.users.colin = {
@ -148,7 +211,6 @@ and populate `pinephone.nix` as such:
# make this numeric so that you can enter it in the phosh lockscreen.
# DON'T leave this empty: not all greeters support passwordless users.
initialPassword = "147147";
shell = pkgs.zsh;
extraGroups = [ "wheel" ];
};
@ -162,48 +224,6 @@ and populate `pinephone.nix` as such:
permitRootLogin = "no";
passwordAuthentication = true;
};
## configure a basic GUI:
services.xserver.desktopManager.phosh = {
enable = true;
user = "colin";
group = "users";
};
hardware.opengl.enable = true;
hardware.opengl.driSupport = true;
## set up home-manager and some useful packages:
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.colin = {
home.stateVersion = "21.11";
home.username = "colin";
home.homeDirectory = "/home/colin";
programs = {
home-manager.enable = true;
firefox.enable = true;
};
# a few useful packages to start with
packages = with pkgs; [
# useful CLI/admin tools to have during setup
fatresize
gptfdisk
networkmanager
sudo
vim
wget
plasma5Packages.konsole
# desktop goodies
element-desktop
gnome-podcasts
vlc
];
};
}
```
@ -244,10 +264,15 @@ we don't want to re-flash the device _every_ time we change something. let's upd
...
```
copy your flake over to the phone at `/etc/nixos/flake.nix`. i use git for this. on the phone:
you may need network access for this. unless you connect a USB-C keyboard, you might find have trouble with the graphical network manager. if so, open a terminal and run:
```sh
~$ git clone https://git.uninsane.org/colin/nix-files.git
~$ sudo rmdir /etc/nixos && sudo mv nix-files /etc/nixos
sudo nmcli device wifi connect <wifiname> password <password>
```
copy your flake over to the phone at `/etc/nixos/flake.nix`. i use git for this. on the phone (or over ssh -- try `ssh nixos` from the same LAN or use `ip addr` to find its IP):
```sh
~$ git clone https://git.uninsane.org/colin/nixos-pinephone-getting-started.git
~$ sudo rmdir /etc/nixos && sudo mv nixos-pinephone-getting-started /etc/nixos
~$ cd /etc/nixos
/etc/nixos$ sudo git config --global --add safe.directory /etc/nixos
/etc/nixos$ sudo nixos-rebuild --flake "./#pinephone" switch
@ -258,12 +283,14 @@ validate this with a reboot, and you should be golden! i recommend mirroring you
happy nixing :-)
# Troubleshooting
## Troubleshooting
- **device is unbootable or unflashable (eMMC isn't exposed as a /dev node over USB):**
these issues tend to be power-related. flash a minimal, known-good image (like [postmarketOS](https://images.postmarketos.org/pinephone/)) to the SD card and boot with the battery and USB charger plugged in. wait until the battery gets to a good charge and resume.
if it's still not working, try holding the reset button underneat the back cover of the phone for 10s.
- **fs appears to be corrupted:**
validate the built, but unflashed, image:
@ -282,27 +309,12 @@ then flash the image using the dd flags i show in the article. while the SD card
finally, the eMMC has a dozen or so write cycles: prefer an SD card.
- **i want to build a new nixos generation on the device but it needs network access:**
if you included `networkmanager` in the package list, open the `konsole` terminal, type `sudo nmtui` and follow the prompts. or directly use `nmcli device wifi connect <wifiname>`. or plug in a USB-C ethernet dongle to get a hardwired connection.
- **the nix config won't build:**
nixos moves fast, but does occasionally break. you might need to freeze the nixpkgs to a specific commit. `mobile-nixos` pins its own pkgs. if you look at [`pkgs.nix`](https://github.com/NixOS/mobile-nixos/blob/master/pkgs.nix) you can copy the rev and track it:
```diff -u a/flake.nix b/flake.nix
inputs = {
- nixpkgs.url = "nixpkgs/nixos-22.05";
+ nixpkgs.url = "nixpkgs/dfd82985c273aac6eced03625f454b334daae2e8";
mobile-nixos = {
url = "github:nixos/mobile-nixos";
flake = false;
};
};
```
nixos moves fast, but does occasionally break. you might need to freeze the nixpkgs to a specific commit. try cloning the [repo](https://git.uninsane.org/colin/nixos-pinephone-getting-started) for this post and building it directly -- the repo includes `flake.lock` so that it should be more reproducible.
# Additional Resources:
## Additional Resources:
- Tom Fitzhenry [building a minimal Pinephone image](https://git.sr.ht/~tomf/notes/tree/master/item/pinephone-nixos-getting-started.md) by dropping his config straight into a mobile-nixos checkout, no `flake.nix` required.
- Ana, Hoverbear on [making an install image](https://hoverbear.org/blog/nix-flake-live-media/) from an existing NixOS configuration.