├── .gitignore ├── dtbs ├── bcm2710-rpi-3-b.dtb ├── bcm2710-rpi-3-b-plus.dtb ├── nixos2205 │ ├── bcm2837-rpi-3-b.dtb │ └── bcm2837-rpi-3-b-plus.dtb ├── checksums ├── download.sh └── README.md ├── nixos ├── .gitignore ├── flake.nix ├── inputrc.nix ├── hardware-configuration.nix └── configuration-sample.nix ├── config-sample.env ├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── rebuild.sh ├── copy-kernel.sh ├── LICENSE ├── flake.lock ├── inspect.sh ├── burn.sh ├── nixos.sh ├── flake.nix ├── README.md └── btrfs-sd-image.nix /.gitignore: -------------------------------------------------------------------------------- 1 | /*.img 2 | /*.iso 3 | /*.bin 4 | /result 5 | /config.env 6 | /build.log 7 | -------------------------------------------------------------------------------- /dtbs/bcm2710-rpi-3-b.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n8henrie/nixos-btrfs-pi/HEAD/dtbs/bcm2710-rpi-3-b.dtb -------------------------------------------------------------------------------- /dtbs/bcm2710-rpi-3-b-plus.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n8henrie/nixos-btrfs-pi/HEAD/dtbs/bcm2710-rpi-3-b-plus.dtb -------------------------------------------------------------------------------- /dtbs/nixos2205/bcm2837-rpi-3-b.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n8henrie/nixos-btrfs-pi/HEAD/dtbs/nixos2205/bcm2837-rpi-3-b.dtb -------------------------------------------------------------------------------- /nixos/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !configuration-sample.nix 4 | !hardware-configuration.nix 5 | !inputrc.nix 6 | !flake.nix 7 | -------------------------------------------------------------------------------- /dtbs/nixos2205/bcm2837-rpi-3-b-plus.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n8henrie/nixos-btrfs-pi/HEAD/dtbs/nixos2205/bcm2837-rpi-3-b-plus.dtb -------------------------------------------------------------------------------- /config-sample.env: -------------------------------------------------------------------------------- 1 | export OUTDEV="/dev/disk/by-id/usb-TS-RDF5_SD_Transcend_000000000039-0:0" 2 | alias umountall='sudo umount -R /mnt/tmp /mnt/btrfs-root /tmp/inspect*; sudo losetup -D;' 3 | -------------------------------------------------------------------------------- /dtbs/checksums: -------------------------------------------------------------------------------- 1 | b008b8809587cccc5d949baa17dfd769e4ae23b45c15317de64423e4942f0c44 bcm2710-rpi-3-b.dtb 2 | e9edb014025f05c5fa746a6e8a925f4ea3695d631dda0bfb9bd502aa76f20b7c bcm2710-rpi-3-b-plus.dtb 3 | -------------------------------------------------------------------------------- /dtbs/download.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -Eeu -o pipefail 4 | 5 | main() { 6 | sha256sum -c checksums && exit 0 7 | 8 | local urls 9 | urls=( 10 | "https://github.com/raspberrypi/firmware/blob/master/boot/bcm2710-rpi-3-b.dtb?raw=true" 11 | "https://github.com/raspberrypi/firmware/blob/master/boot/bcm2710-rpi-3-b-plus.dtb?raw=true" 12 | ) 13 | 14 | local url 15 | for url in "${urls[@]}"; do 16 | wget --content-disposition "${url}" 17 | done 18 | 19 | sha256sum -- *.dtb > checksums 20 | sha256sum -c checksums 21 | } 22 | main "$@" 23 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [n8henrie] 4 | custom: ["https://n8henrie.com/donate"] 5 | patreon: n8henrie 6 | open_collective: # Replace with a single Open Collective username 7 | ko_fi: # Replace with a single Ko-fi username 8 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | liberapay: n8henrie 11 | issuehunt: # Replace with a single IssueHunt username 12 | otechie: # Replace with a single Otechie username 13 | -------------------------------------------------------------------------------- /rebuild.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # rebuild.sh: Deletes the images, tries to do some garbage collection and 3 | # delete a few dependencies from the nix store, then runs `build.sh`. Useful 4 | # when it seemed that my changes to `.nix` files weren't being picked up for 5 | # whatever reason. 6 | 7 | set -Eeuf -o pipefail 8 | set -x 9 | 10 | main() { 11 | rm -f ./btrfspi.iso ./result 12 | find /nix/store \ 13 | \( -name '*nixos-btrfs*' -o -name '*btrfs-fs*' -o -name '*btrfspi*' \) \ 14 | -exec nix store delete --ignore-liveness -v {} + 15 | sudo -u "${SUDO_USER}" ./build.sh 16 | } 17 | main "$@" 18 | -------------------------------------------------------------------------------- /dtbs/README.md: -------------------------------------------------------------------------------- 1 | # This doesn't work yet 2 | 3 | At least `download.sh` isn't there. Apparently the dtbs have to be compiled 4 | from the linux source, I don't feel like doing that yet. 5 | 6 | The `dtbs/nixos2205` are just copied from a raspberry pi SD image. 7 | 8 | NB: There is some disagreement between the names and what we're actually 9 | getting; for example: 10 | 11 | 12 | 13 | ``` 14 | copyDTB bcm2710-rpi-3-b.dtb bcm2837-rpi-3-b.dtb 15 | copyDTB bcm2710-rpi-3-b-plus.dtb bcm2837-rpi-3-a-plus.dtb 16 | copyDTB bcm2710-rpi-3-b-plus.dtb bcm2837-rpi-3-b-plus.dtb 17 | ``` 18 | -------------------------------------------------------------------------------- /copy-kernel.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # copy-kernel.sh: Convenience script to mount the image and copy the kernel 3 | # locally for use with `nixos.sh`. Delete `./u-boot-rpi3.bin` to copy a fresh 4 | # version next run. 5 | 6 | set -Eeuf -o pipefail 7 | set -x 8 | 9 | loopdev= 10 | tmpmount= 11 | 12 | cleanup() { 13 | mountpoint "${tmpmount}" && umount -R "${tmpmount}" 14 | [[ -n "${loopdev}" ]] && losetup -d "${loopdev}" 15 | } 16 | trap cleanup EXIT 17 | 18 | main() { 19 | if [[ "${EUID}" -ne 0 ]]; then 20 | sudo "$0" "$@" 21 | exit $? 22 | fi 23 | 24 | local img=${1:-./result/btrfspi.iso} 25 | tmpmount=$(mktemp -d) 26 | loopdev=$(losetup --find --partscan --show "${img}") 27 | 28 | mount "${loopdev}p1" "${tmpmount}" 29 | cp "${tmpmount}/u-boot-rpi3.bin" . 30 | } 31 | main "$@" 32 | -------------------------------------------------------------------------------- /nixos/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "RPi3 (or 4) on BTRFS root"; 3 | inputs = { 4 | nixpkgs.url = "nixpkgs/nixos-23.05"; 5 | nixos-hardware.url = "github:NixOS/nixos-hardware/master"; 6 | }; 7 | outputs = { 8 | self, 9 | nixpkgs, 10 | ... 11 | } @ inputs: let 12 | system = "aarch64-linux"; 13 | in { 14 | nixosConfigurations = { 15 | nixpi = 16 | nixpkgs.lib.nixosSystem 17 | { 18 | inherit system; 19 | modules = [ 20 | ./configuration.nix 21 | ]; 22 | }; 23 | nixpi4 = 24 | nixpkgs.lib.nixosSystem 25 | { 26 | inherit system; 27 | modules = [ 28 | ./configuration.nix 29 | inputs.nixos-hardware.nixosModules.raspberry-pi-4 30 | ]; 31 | }; 32 | }; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nathan Henrie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixos-hardware": { 4 | "locked": { 5 | "lastModified": 1689320556, 6 | "narHash": "sha256-vODUkZLWFVCvo1KPK3dC2CbXjxa9antEn5ozwlcTr48=", 7 | "owner": "NixOS", 8 | "repo": "nixos-hardware", 9 | "rev": "d4ea64f2063820120c05f6ba93ee02e6d4671d6b", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "master", 15 | "repo": "nixos-hardware", 16 | "type": "github" 17 | } 18 | }, 19 | "nixpkgs": { 20 | "locked": { 21 | "lastModified": 1689885880, 22 | "narHash": "sha256-2ikAcvHKkKh8J/eUrwMA+wy1poscC+oL1RkN1V3RmT8=", 23 | "owner": "NixOS", 24 | "repo": "nixpkgs", 25 | "rev": "fa793b06f56896b7d1909e4b69977c7bf842b2f0", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "id": "nixpkgs", 30 | "ref": "nixos-23.05", 31 | "type": "indirect" 32 | } 33 | }, 34 | "root": { 35 | "inputs": { 36 | "nixos-hardware": "nixos-hardware", 37 | "nixpkgs": "nixpkgs" 38 | } 39 | } 40 | }, 41 | "root": "root", 42 | "version": 7 43 | } 44 | -------------------------------------------------------------------------------- /inspect.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # inspect.sh: Convenience script to set up loop devices which are then mounted 3 | # to some temporary directories. Don't forget to `umount` and `losetup -D` 4 | # afterwards 5 | 6 | set -Eeuf -o pipefail 7 | set -x 8 | 9 | loopdev= 10 | readonly dest=/tmp/inspect 11 | 12 | cleanup() { 13 | umount -R "${dest}?" || true 14 | [[ -n "${loopdev}" ]] && losetup -d "${loopdev}" 15 | } 16 | trap cleanup INT TERM ERR 17 | 18 | main() { 19 | if [[ "${EUID}" -ne 0 ]]; then 20 | sudo bash "$0" "$@" 21 | exit $? 22 | fi 23 | 24 | local img=${1:-./btrfspi.iso} 25 | 26 | local loopnum loopdev 27 | loopdev=$(losetup --find --partscan --show "${img}") 28 | loopnum=${loopdev#/dev/loop} 29 | 30 | local parts 31 | mapfile -t parts < <(find /dev -name "${loopdev#/dev/}p*") 32 | 33 | for part in "${parts[@]}"; do 34 | partnum=${part#"${loopdev}p"} 35 | 36 | partdest="${dest}_${loopnum}_${partnum}" 37 | mkdir -p "${partdest}" 38 | mountpoint "${partdest}" && { 39 | echo "already mounted!" 40 | exit 1 41 | } 42 | mount "${part}" "${partdest}" || echo "Unable to mount ${part}" 43 | done 44 | echo "mounted at ${partdest}" 45 | } 46 | main "$@" 47 | -------------------------------------------------------------------------------- /burn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # burn.sh: Wrapper around `dd` to write the image to my SD card after I somehow 3 | # blew away a different drive (recovered thank goodness for ZFS) one time, then 4 | # another time wrote everything out to a file named `/dev/sde` and couldn't 5 | # figure out why the SD card wouldn't boot. 6 | 7 | set -Eeuf -o pipefail 8 | set -x 9 | 10 | err() { 11 | log "$*" 12 | exit 1 13 | } 14 | 15 | log() { 16 | printf '%s\n' "$*" > /dev/stderr 17 | } 18 | 19 | main() { 20 | if [[ "${EUID}" -ne 0 ]]; then 21 | sudo "$0" "$@" 22 | exit $? 23 | fi 24 | source config.env 25 | 26 | local img outdev 27 | img=${1:-./result/btrfspi.iso.zst} 28 | 29 | outdev=${OUTDEV:-"/dev/disk/by-id/usb-TS-RDF5_SD_Transcend_000000000039-0:0"} 30 | [[ -b "${outdev}" ]] || err "device not found: ${outdev}" 31 | 32 | if [[ "${img}" =~ .*\.zst$ ]]; then 33 | zstd --decompress --stdout "${img}" | 34 | dd \ 35 | of="${outdev}" \ 36 | bs=4M \ 37 | status=progress \ 38 | conv=fsync 39 | else 40 | dd \ 41 | if="${img}" \ 42 | of="${outdev}" \ 43 | bs=4M \ 44 | status=progress \ 45 | conv=fsync 46 | fi 47 | noti -m "burn done" 48 | } 49 | main "$@" 50 | -------------------------------------------------------------------------------- /nixos/inputrc.nix: -------------------------------------------------------------------------------- 1 | { 2 | environment.etc."inputrc".text = '' 3 | # Modified from default 20200522 4 | 5 | set meta-flag on 6 | set input-meta on 7 | set convert-meta off 8 | set output-meta on 9 | set colored-stats on 10 | 11 | set show-all-if-ambiguous on 12 | set completion-ignore-case on 13 | set completion-map-case on 14 | set bell-style none 15 | set echo-control-characters off 16 | 17 | "\e[A": history-search-backward 18 | "\e[B": history-search-forward 19 | 20 | #set mark-symlinked-directories on 21 | 22 | $if mode=emacs 23 | 24 | # for linux console and RH/Debian xterm 25 | "\e[1~": beginning-of-line 26 | "\e[4~": end-of-line 27 | "\e[5~": beginning-of-history 28 | "\e[6~": end-of-history 29 | "\e[3~": delete-char 30 | "\e[2~": quoted-insert 31 | "\e[5C": forward-word 32 | "\e[5D": backward-word 33 | "\e[1;5C": forward-word 34 | "\e[1;5D": backward-word 35 | 36 | # for rxvt 37 | "\e[8~": end-of-line 38 | 39 | # for non RH/Debian xterm, can't hurt for RH/DEbian xterm 40 | "\eOH": beginning-of-line 41 | "\eOF": end-of-line 42 | 43 | # for freebsd console 44 | "\e[H": beginning-of-line 45 | "\e[F": end-of-line 46 | $endif 47 | ''; 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "build" 2 | on: 3 | pull_request: 4 | push: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Install cross-compilation deps 11 | run: | 12 | sudo apt update 13 | sudo apt install -q -y \ 14 | binfmt-support \ 15 | gcc-aarch64-linux-gnu \ 16 | qemu-system-aarch64 \ 17 | qemu-user-static 18 | - uses: actions/checkout@v3 19 | - uses: cachix/install-nix-action@v24 20 | with: 21 | nix_path: nixpkgs=channel:nixos-23.11 22 | extra_nix_config: | 23 | extra-platforms = aarch64-linux 24 | max-jobs = auto 25 | cores = 0 26 | system-features = big-parallel kvm 27 | experimental-features = nix-command flakes 28 | - uses: cachix/cachix-action@v12 29 | with: 30 | name: nixos-btrfs-pi 31 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 32 | - run: | 33 | nix build --print-build-logs --show-trace 34 | nix show-derivation ./result 35 | env: 36 | GC_DONT_GC: 1 37 | # Workaround for https://github.com/actions/upload-artifact/issues/92 38 | - run: echo "UPLOAD_PATH=$(readlink -f result)" >> $GITHUB_ENV 39 | - uses: actions/upload-artifact@v3 40 | with: 41 | name: btrfspi 42 | path: ${{ env.UPLOAD_PATH }} 43 | - name: Release 44 | uses: softprops/action-gh-release@v1 45 | if: startsWith(github.ref, 'refs/tags/v') 46 | with: 47 | files: ${{ env.UPLOAD_PATH }}/* 48 | -------------------------------------------------------------------------------- /nixos.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Run the image in QEMU 3 | 4 | # $ qemu-system-aarch64 --version 5 | # QEMU emulator version 6.2.0 6 | # Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers 7 | # 8 | # Notes on `console=` and getting terminal output: 9 | # - `-nographic` alone allows seeing kernel messages during boot 10 | # - Changing the serial devices in `-append` doesn't seem to matter, only changing the `kernalParams` results in changing extlinux.conf which takes effect 11 | # - ttyAMA0 seems to allow the kernel messages and systemd messages but no login prompt 12 | # - ttyS0, tty0, ttyUSB0 all seem nonfunctional? no output (perhaps overridden by my qemu args?) 13 | # https://tldp.org/HOWTO/Remote-Serial-Console-HOWTO/configure-kernel.html 14 | # - The console parameter can be given repeatedly, but the parameter can only be given once for each console technology. So console=tty0 console=lp0 console=ttyS0 is acceptable but console=ttyS0 console=ttyS1 will not work. 15 | # - When multiple consoles are listed output is sent to all consoles and input is taken from the last listed console. The last console is the one Linux uses as the /dev/console device. 16 | 17 | set -Eeuf -o pipefail 18 | set -x 19 | 20 | main() { 21 | local img=${1:-./btrfspi.iso} 22 | qemu-system-aarch64 \ 23 | -M raspi3b \ 24 | -m 1G \ 25 | -smp 4 \ 26 | -drive "format=raw,if=sd,file=${img}" \ 27 | -kernel ./u-boot-rpi3.bin \ 28 | -usb -device usb-kbd \ 29 | -device usb-net,netdev=net0 \ 30 | -netdev user,id=net0,hostfwd=tcp::2222-:22 \ 31 | -serial null \ 32 | -serial mon:stdio \ 33 | -append 'root=/dev/mmcblk0p2 rootfstype=btrfs rootflags=subvol=@ rootwait' 34 | } 35 | 36 | main "$@" 37 | 38 | # None of these seem to get the keyboard or SSH working unfortunately. 39 | # -append "console=ttyS0,115200n8 console=ttyAMA0,115200n8 console=tty0" 40 | # -append "rw console=tty1 root=LABEL=NIXOS_SD rootfstype=btrfs" \ 41 | # -device usb-kbd \ 42 | # -device usb-mouse \ 43 | # -device usb-net,netdev=net0 \ 44 | # -dtb ./dtbs/bcm2837-rpi-3-b-plus.dts \ 45 | # -net nic -net user,hostfwd=tcp::2222-:22 \ 46 | # -netdev user,id=net0,hostfwd=tcp::2222-:22 \ 47 | # -nographic \ 48 | # -serial stdio \ 49 | # -usb -device usb-host,hostbus=001,hostaddr=002 \ 50 | -------------------------------------------------------------------------------- /nixos/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | modulesPath, 4 | ... 5 | }: { 6 | imports = [ 7 | (modulesPath + "/installer/scan/not-detected.nix") 8 | ]; 9 | 10 | nixpkgs.overlays = let 11 | ubootWithBtrfsAndZstd = oldAttrs: { 12 | extraConfig = '' 13 | CONFIG_CMD_BTRFS=y 14 | CONFIG_ZSTD=y 15 | 16 | CONFIG_BOOTCOMMAND="setenv boot_prefixes /@boot/ /@/ /boot/ /; run distro_bootcmd;" 17 | ''; 18 | }; 19 | in [ 20 | (self: super: { 21 | ubootRaspberryPi3_64bit = super.ubootRaspberryPi3_64bit.overrideAttrs ubootWithBtrfsAndZstd; 22 | ubootRaspberryPi4_64bit = super.ubootRaspberryPi4_64bit.overrideAttrs ubootWithBtrfsAndZstd; 23 | }) 24 | ]; 25 | 26 | boot = { 27 | # console=ttyAMA0 seems necessary for kernel boot messages in qemu 28 | kernelParams = [ 29 | "console=ttyS0,115200n8" 30 | "console=ttyAMA0,115200n8" 31 | "console=tty0" 32 | "root=/dev/disk/by-label/NIXOS_SD" 33 | "rootfstype=btrfs" 34 | "rootflags=subvol=@" 35 | "rootwait" 36 | ]; 37 | initrd.kernelModules = ["zstd" "btrfs"]; 38 | loader = { 39 | grub.enable = false; 40 | generic-extlinux-compatible = { 41 | enable = true; 42 | configurationLimit = 20; 43 | }; 44 | }; 45 | }; 46 | 47 | fileSystems = let 48 | opts = [ 49 | "noatime" 50 | "ssd_spread" 51 | "autodefrag" 52 | "discard=async" 53 | "compress-force=zstd" 54 | ]; 55 | fsType = "btrfs"; 56 | device = "/dev/disk/by-label/NIXOS_SD"; 57 | in { 58 | "/" = { 59 | inherit fsType device; 60 | options = opts ++ ["subvol=@"]; 61 | }; 62 | "/boot" = { 63 | inherit fsType device; 64 | options = opts ++ ["subvol=@boot"]; 65 | }; 66 | "/gnu" = { 67 | inherit fsType device; 68 | options = opts ++ ["subvol=@gnu"]; 69 | }; 70 | "/nix" = { 71 | inherit fsType device; 72 | options = opts ++ ["subvol=@nix"]; 73 | }; 74 | "/var" = { 75 | inherit fsType device; 76 | options = opts ++ ["subvol=@var"]; 77 | }; 78 | "/home" = { 79 | inherit fsType device; 80 | options = opts ++ ["subvol=@home"]; 81 | }; 82 | "/.snapshots" = { 83 | inherit fsType device; 84 | options = opts ++ ["subvol=@snapshots"]; 85 | }; 86 | "/boot/firmware" = { 87 | device = "/dev/disk/by-label/FIRMWARE"; 88 | fsType = "vfat"; 89 | options = ["nofail" "noauto"]; 90 | }; 91 | }; 92 | 93 | zramSwap = { 94 | enable = true; 95 | memoryPercent = 50; 96 | algorithm = "zstd"; 97 | }; 98 | 99 | powerManagement.cpuFreqGovernor = 100 | lib.mkDefault 101 | "ondemand"; 102 | } 103 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "sdimage for RPi3 on BTRFS root"; 3 | inputs = { 4 | nixpkgs.url = "nixpkgs/nixos-23.05"; 5 | nixos-hardware.url = "github:NixOS/nixos-hardware/master"; 6 | }; 7 | outputs = { 8 | self, 9 | nixpkgs, 10 | ... 11 | } @ inputs: let 12 | system = "x86_64-linux"; 13 | armSystem = "aarch64-linux"; 14 | in { 15 | packages.${system} = let 16 | pkgs = nixpkgs.legacyPackages.${system}; 17 | pkgsArm = nixpkgs.legacyPackages.${armSystem}; 18 | ubootPkgs = 19 | (import nixpkgs 20 | { 21 | localSystem.system = system; 22 | crossSystem.system = armSystem; 23 | inherit 24 | ((import ./nixos/hardware-configuration.nix 25 | { 26 | inherit (pkgsArm) lib; 27 | pkgs = pkgsArm; 28 | modulesPath = pkgsArm.path + "/nixos/modules"; 29 | }) 30 | .nixpkgs) 31 | overlays 32 | ; 33 | }) 34 | .pkgs; 35 | btrfsPi = {piVersion ? 3}: 36 | import ./btrfs-sd-image.nix { 37 | inherit inputs pkgs piVersion; 38 | bootFromBTRFS = true; 39 | BTRFSDupData = false; 40 | subvolumes = ["@" "@boot" "@gnu" "@home" "@nix" "@snapshots" "@var"]; 41 | }; 42 | in { 43 | default = self.outputs.packages.${system}.btrfsPi3; 44 | btrfsPi3 = btrfsPi {}; 45 | btrfsPi4 = btrfsPi {piVersion = 4;}; 46 | uboot3 = ubootPkgs.ubootRaspberryPi3_64bit; 47 | uboot4 = ubootPkgs.ubootRaspberryPi4_64bit; 48 | runVm = let 49 | uboot = 50 | (import nixpkgs 51 | { 52 | localSystem.system = system; 53 | crossSystem.system = armSystem; 54 | inherit 55 | ((import ./nixos/hardware-configuration.nix 56 | { 57 | inherit (pkgsArm) lib; 58 | pkgs = pkgsArm; 59 | modulesPath = pkgsArm.path + "/nixos/modules"; 60 | }) 61 | .nixpkgs) 62 | overlays 63 | ; 64 | }) 65 | .pkgs 66 | .ubootRaspberryPi3_64bit; 67 | in 68 | pkgs.writeScript "run-nixos-vm" '' 69 | #!${pkgs.runtimeShell} 70 | 71 | img=aarch64-qemu.img 72 | zstd \ 73 | --decompress \ 74 | ${self.outputs.packages.${system}.default}/*.iso.zst \ 75 | -o "$img" 76 | chmod 0640 "$img" 77 | qemu-img resize -f raw "$img" 4G 78 | 79 | qemu-system-aarch64 \ 80 | -machine raspi3b \ 81 | -kernel "${uboot}/u-boot.bin" \ 82 | -cpu max \ 83 | -m 1G \ 84 | -smp 4 \ 85 | -drive file="$img",format=raw \ 86 | -device usb-net,netdev=net0 \ 87 | -netdev user,id=net0,hostfwd=tcp::2222-:22 \ 88 | -serial null \ 89 | -serial mon:stdio 90 | ''; 91 | }; 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /nixos/configuration-sample.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | lib, 4 | ... 5 | }: { 6 | imports = [ 7 | ./inputrc.nix 8 | ./hardware-configuration.nix 9 | ]; 10 | 11 | networking = { 12 | hostName = "nixpi"; 13 | domain = "home.arpa"; 14 | useDHCP = false; 15 | interfaces = { 16 | eth0.useDHCP = true; 17 | wlan0.useDHCP = true; 18 | }; 19 | firewall.enable = false; 20 | wireless = { 21 | enable = true; 22 | interfaces = ["wlan0"]; 23 | networks = {}; 24 | }; 25 | }; 26 | 27 | time.timeZone = "America/Denver"; 28 | 29 | services = { 30 | timesyncd.enable = true; 31 | avahi = { 32 | enable = true; 33 | publish = { 34 | enable = true; 35 | addresses = true; 36 | }; 37 | }; 38 | openssh = { 39 | enable = true; 40 | settings.PermitRootLogin = "yes"; 41 | }; 42 | fstrim.enable = true; 43 | nscd.enableNsncd = true; 44 | }; 45 | 46 | systemd.services = { 47 | wpa_supplicant.wantedBy = lib.mkOverride 10 ["default.target"]; 48 | sshd.wantedBy = lib.mkOverride 40 ["multi-user.target"]; 49 | }; 50 | 51 | environment = { 52 | systemPackages = with pkgs; [ 53 | compsize 54 | git 55 | libraspberrypi 56 | neovim 57 | nixpkgs-fmt 58 | tmux 59 | wget 60 | # (import ./vim.nix { inherit pkgs; }) 61 | # (import ./nvim.nix { inherit pkgs; }) 62 | ]; 63 | variables = { 64 | EDITOR = "nvim"; 65 | }; 66 | }; 67 | 68 | users = { 69 | mutableUsers = false; 70 | users = { 71 | root = { 72 | password = "nixos-btrfs"; 73 | openssh.authorizedKeys.keyFiles = [ 74 | (builtins.fetchurl { 75 | url = "https://github.com/n8henrie.keys"; 76 | sha256 = "0dds17syy384lh3j0bm7k4aqqam7b67m3c6kk86gq0hfjd2vv231"; 77 | }) 78 | ]; 79 | }; 80 | }; 81 | }; 82 | 83 | i18n.supportedLocales = ["en_US.UTF-8/UTF-8"]; 84 | 85 | nixpkgs.config.allowUnfree = true; 86 | nix = { 87 | # Disable nix channel lookups, use flakes instead 88 | nixPath = []; 89 | settings = { 90 | max-jobs = "auto"; 91 | auto-optimise-store = true; 92 | cores = 0; 93 | }; 94 | extraOptions = '' 95 | experimental-features = nix-command flakes 96 | ''; 97 | }; 98 | 99 | system = { 100 | autoUpgrade = { 101 | enable = true; 102 | flake = "/etc/nixos"; 103 | flags = [ 104 | "--update-input" 105 | "nixpkgs" 106 | "--commit-lock-file" 107 | ]; 108 | dates = "02:00"; 109 | allowReboot = true; 110 | rebootWindow = { 111 | upper = "03:00"; 112 | lower = "04:00"; 113 | }; 114 | }; 115 | 116 | # This value determines the NixOS release from which the default 117 | # settings for stateful data, like file locations and database versions 118 | # on your system were taken. It‘s perfectly fine and recommended to leave 119 | # this value at the release version of the first install of this system. 120 | # Before changing this value read the documentation for this option 121 | # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). 122 | stateVersion = "22.11"; # Did you read the comment? 123 | }; 124 | } 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building a BTRFS-root NixOS on a Raspberry Pi 3 or 4 2 | 3 | This repo contains tools to build an image for a Raspberry Pi 3 or 4 on a BTRFS 4 | root. I have been using these scripts to build from my Arch-based x86_64 server 5 | and they work pretty well. You can probably get a good idea of how to do the 6 | same on e.g. Ubuntu, but you're on your own with regards to QEMU and nix 7 | installation. 8 | 9 | I have a decent threadripper with plenty of ram and fairly slow internet; it 10 | takes my machine about 20 minutes to build the image. 11 | 12 | ## Quickstart 13 | 14 | ```console 15 | $ sudo pacman -S nix 16 | $ yay -S qemu-user-static-bin 17 | $ cat <<'EOF' | sudo tee -a /etc/nix/nix.conf 18 | extra-platforms = aarch64-linux 19 | experimental-features = nix-command flakes repl-flake 20 | max-jobs = auto 21 | cores = 0 22 | EOF 23 | $ sudo systemctl restart nix-daemon.service 24 | $ git clone https://github.com/n8henrie/nixos-btrfs-pi && cd nixos-btrfs-pi 25 | $ nix build 26 | ``` 27 | 28 | This should give you `./result/btrfspi.img.zst` 29 | 30 | - Test run in QEMU: 31 | - `nix build .#runVm && ./result` 32 | - You can also look at `nixos.sh`, which works similarly, but requires: 33 | - You'll first need to `qemu-img resize`, which requires ownership 34 | - You'll also need a copy of the `dtb` file and kernel 35 | - I've scripted builting + these steps into `build.sh` 36 | - In QEMU, I can't get the keyboard to work consistently (once in a blue 37 | moon via `device_add usb-host,hostbus=001,hostaddr=002`) or SSH to work 38 | at all 39 | - Burn to your sd card: `sudo ./burn.sh` 40 | - To be save you might want to set your `OUTDEV` in `config.env` and source 41 | this first 42 | - Boot it up 43 | 44 | ## Configuration 45 | 46 | This image is on BTRFS, using the subvolumes as specified in 47 | `btrfs-sd-image.nix`. The root subvolume is `@`, home is `@home`, etc. 48 | 49 | The `FAT`-based `FIRMWARE` partition has important Raspberry Pi config files 50 | such as `config.txt` and can be mounted to its default location with `mount 51 | /boot/firmware`. To help protect these critical files it is not mounted by 52 | default. 53 | 54 | ### `bootFromBTRFS` 55 | 56 | When `true`, this option puts the boot files into the `@boot` subvolume, which 57 | gets mounted at `/boot`. When `false`, the boot files go onto the `FAT`-based 58 | `FIRMWARE` partition. See the **Booting** section below for additional details. 59 | 60 | ### `BTRFSDupData` 61 | 62 | This option runs `btrfs balance start -dconvert=DUP /` on the system's first 63 | boot, duplicating all data on the SD card. Please see the `DUP PROFILES ON A 64 | SINGLE DEVICE` section of `man mkfs.btrfs` for additional details; I'm not sure 65 | whether this would increase or harm the robustness of a NixOS system on an SD 66 | card. 67 | 68 | Please note that setting data to `DUP` seems to be incompatible with booting 69 | directly from BTRFS, so one must set `bootFromBTRFS` to `false`. (If you are 70 | booting from the `FAT` partition but did not set `BTRFSDupData` to true, you 71 | can choose to convert your data to `DUP` at any time.) 72 | 73 | ## Booting 74 | 75 | By default this image has `@boot` mounted to `/boot` and the initrd and 76 | required boot files are installed there. It uses a patched u-boot that has 77 | support for BTRFS and zstd compression. 78 | 79 | Unfortunately, u-boot doesn't seem to work from a compressed subvolume for 80 | whatever reason; after over a year or work I've basically given up: 81 | 82 | - 83 | - 84 | 85 | For now, the workaround is to disable compression (and COW) via `chattr +C` on 86 | this subvolume. (You can also `btrfs property set /boot compression none`, but 87 | this gets overridden and breaks if one uses the `compress-force` mount option, 88 | where as `chattr +C` works even then). 89 | 90 | If anyone has other ideas on how I can get u-boot to boot from `@boot` without 91 | disabling compression, I'd be interested to hear about it. 92 | 93 | ## WIP / Known issues / Notes 94 | 95 | - I'd still love to figure out why I can't boot from my zstd-compressed `@boot` 96 | BTRFS subvolume; seems like u-boot supports the right stuff 97 | - I spent a good while putting together a flake that would use 98 | `nixos-generators` to create a standard sdImage based on 99 | `nixos/configuration-sample.nix` (which includes BTRFS kernel modules), use 100 | the u-boot strategy from this repo, then copy the contents and updated u-boot 101 | over to a blank BTRFS-based partition. It ran *much* faster than this 102 | approach, but wouldn't boot. Why doesn't this work? 103 | 104 | ### Debugging 105 | 106 | This seems handy. Still trying to figure out how to inspect the value of 107 | `fileSystems` and whether I'm setting it correctly. 108 | 109 | ```console 110 | $ nix show-derivation \ 111 | --include nixos-config=sd-image.nix \ 112 | --argstr system aarch64-linux \ 113 | --file '' \ 114 | config.system.build.sdImage 115 | ``` 116 | 117 | **UPDATE 20220301:** Finally figured out how to debug a value: 118 | 119 | 120 | ```console 121 | $ nix repl \ 122 | --include nixos-config=./sd-image.nix \ 123 | --argstr system aarch64-linux 124 | '' 125 | nix-repl> :p config.fileSystems 126 | ``` 127 | 128 | Or even better: 129 | 130 | ```console 131 | $ nix eval \ 132 | --include nixos-config=./sd-image.nix \ 133 | --argstr system aarch64-linux \ 134 | --file '' \ 135 | config.fileSystems 136 | ``` 137 | 138 | With color and formatting in a pager: 139 | 140 | ```console 141 | $ nix eval \ 142 | --include nixos-config=./sd-image.nix \ 143 | --argstr system aarch64-linux \ 144 | --file '' \ 145 | config.fileSystems \ 146 | --json | 147 | jq --color-output | 148 | bat 149 | ``` 150 | 151 | ## Overview of shell scripts in this repo 152 | 153 | - `inspect.sh`: Convenience script to set up loop devices which are then 154 | mounted to some temporary directories. Don't forget to `umount` and `losetup 155 | -D` afterwards 156 | - `nixos.sh`: Run qemu to see if the image boots (see also the `.#runVm` flake 157 | output) 158 | - `copy-kernel.sh`: Convenience script to mount the image and copy the kernel 159 | locally for use with `nixos.sh`. Delete `./u-boot-rpi3.bin` to copy a fresh 160 | version next run. 161 | - `build.sh`: Runs `nix build`, makes a user-owned copy of the image, resizes 162 | image, runs `nixos.sh` 163 | - `rebuild.sh`: Deletes the images, tries to do some garbage collection and 164 | delete a few dependencies from the nix store, then runs `build.sh`. Useful 165 | when it seemed that my changes to `.nix` files weren't being picked up for 166 | whatever reason. 167 | - `burn.sh`: Wrapper around `dd` to write the image to my SD card after I 168 | somehow blew away a different drive (recovered thank goodness for ZFS) one 169 | time, then another time wrote everything out to a file named `/dev/sde` and 170 | couldn't figure out why the SD card wouldn't boot. 171 | - `dtbs/download.sh`: Not currently functional 172 | 173 | NB: I've given myself `NOPASSWD` permissions to run the following so that I can 174 | fire and forget `./build.sh`: 175 | 176 | - `burn.sh` 177 | - `copy-kernel.sh` 178 | - `inspect.sh` 179 | - `rebuild.sh` 180 | 181 | ## License 182 | 183 | My changes and modifications are MIT as per `/LICENSE`, to the extent 184 | permitted. 185 | 186 | Substantial portions of this project were copied from: 187 | 188 | - 189 | - 190 | 191 | ## Learning Resources 192 | 193 | - https://github.com/lucernae/nixos-pi/blob/main/README.md 194 | -------------------------------------------------------------------------------- /btrfs-sd-image.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | inputs, 4 | bootFromBTRFS ? true, 5 | BTRFSDupData ? false, 6 | subvolumes ? ["@" "@boot" "@gnu" "@home" "@nix" "@snapshots" "@var"], 7 | piVersion ? 3, 8 | }: let 9 | pkgsArm = import pkgs.path { 10 | localSystem.system = "aarch64-linux"; 11 | }; 12 | pkgsCross = import pkgs.path { 13 | localSystem.system = "x86_64-linux"; 14 | crossSystem.system = "aarch64-linux"; 15 | }; 16 | 17 | extraConfigTxt = [ 18 | "gpu_mem=16" 19 | "program_usb_boot_mode=1" 20 | "program_usb_boot_timeout=1" 21 | ]; 22 | 23 | btrfspi = import (pkgs.path + "/nixos/lib/eval-config.nix") { 24 | system = "aarch64-linux"; 25 | specialArgs = {inherit inputs;}; 26 | modules = [ 27 | { 28 | imports = 29 | [ 30 | ./nixos/configuration-sample.nix 31 | ] 32 | ++ pkgs.lib.optional (piVersion == 4) inputs.nixos-hardware.nixosModules.raspberry-pi-4; 33 | 34 | boot.postBootCommands = with pkgsCross; '' 35 | # On the first boot do some maintenance tasks 36 | set -Eeuf -o pipefail 37 | 38 | if [ -f /nix-path-registration ]; then 39 | # Figure out device names for the boot device and root filesystem. 40 | rootPart=$(${pkgsArm.util-linux}/bin/findmnt -nvo SOURCE /) 41 | firmwareDevice=$(lsblk -npo PKNAME $rootPart) 42 | partNum=$( 43 | lsblk -npo MAJ:MIN "$rootPart" | 44 | ${gawk}/bin/awk -F: '{print $2}' | 45 | tr -d '[:space:]' 46 | ) 47 | 48 | # Resize the root partition and the filesystem to fit the disk 49 | echo ',+,' | sfdisk -N"$partNum" --no-reread "$firmwareDevice" 50 | ${parted}/bin/partprobe 51 | ${btrfs-progs}/bin/btrfs filesystem resize max / 52 | 53 | if [ ${toString BTRFSDupData} ]; then 54 | ${btrfs-progs}/bin/btrfs balance start -dconvert=DUP / 55 | fi 56 | 57 | # Register the contents of the initial Nix store 58 | ${btrfspi.config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration 59 | 60 | # Prevents this from running on later boots. 61 | rm -f /nix-path-registration 62 | fi 63 | ''; 64 | } 65 | ]; 66 | }; 67 | 68 | inherit (btrfspi.config.system.build) toplevel; 69 | channelSources = let 70 | nixpkgs = pkgs.lib.cleanSource pkgs.path; 71 | in 72 | pkgs.runCommand "nixos-${btrfspi.config.system.nixos.version}" {} '' 73 | mkdir -p $out 74 | cp -prd ${nixpkgs.outPath} $out/nixos 75 | chmod -R u+w $out/nixos 76 | if [ ! -e $out/nixos/nixpkgs ]; then 77 | ln -s . $out/nixos/nixpkgs 78 | fi 79 | rm -rf $out/nixos/.git 80 | echo -n ${btrfspi.config.system.nixos.versionSuffix} > $out/nixos/.version-suffix 81 | ''; 82 | 83 | closure = pkgs.closureInfo { 84 | rootPaths = [toplevel channelSources]; 85 | }; 86 | 87 | firmwarePartOpts = let 88 | opts = { 89 | inherit (btrfspi) pkgs config; 90 | inherit (btrfspi.pkgs) lib; 91 | }; 92 | inherit ((import (pkgsCross.path + "/nixos/modules/installer/sd-card/sd-image.nix") opts).options) sdImage; 93 | sdImageAarch64 = import (pkgsCross.path + "/nixos/modules/installer/sd-card/sd-image-aarch64.nix"); 94 | in { 95 | firmwarePartID = sdImage.firmwarePartitionID.default; 96 | firmwarePartName = sdImage.firmwarePartitionName.default; 97 | inherit ((sdImageAarch64 opts).sdImage) populateFirmwareCommands; 98 | 99 | inherit 100 | ( 101 | (import (pkgs.path + "/nixos/modules/system/boot/loader/generic-extlinux-compatible") { 102 | inherit pkgs; 103 | inherit (pkgs) lib; 104 | inherit (btrfspi) config; 105 | }) 106 | .config 107 | .content 108 | .boot 109 | .loader 110 | .generic-extlinux-compatible 111 | ) 112 | populateCmd 113 | ; 114 | }; 115 | 116 | # Take contents of ./nixos/*.nix and make list of derivations 117 | configFiles = with builtins; 118 | dir: 119 | attrValues (mapAttrs 120 | ( 121 | name: value: let 122 | filename = 123 | if name == "configuration-sample.nix" 124 | then "configuration.nix" 125 | else name; 126 | in 127 | pkgs.writeTextDir "share/${filename}" (readFile "${dir}/${name}") 128 | ) 129 | (pkgs.lib.filterAttrs (n: _: pkgs.lib.strings.hasSuffix ".nix" n) (readDir dir))); 130 | in 131 | assert pkgs.lib.assertMsg (!(bootFromBTRFS && BTRFSDupData)) "bootFromBTRFS and BTRFSDupData are mutually exclusive"; 132 | pkgs.vmTools.runInLinuxVM 133 | (pkgs.runCommand "btrfspi-sd" 134 | { 135 | enableParallelBuildingByDefault = true; 136 | nativeBuildInputs = with pkgs; [ 137 | btrfs-progs 138 | dosfstools 139 | e2fsprogs 140 | git # initialize repo at `/etc/nixos` 141 | nix # mv, cp 142 | util-linux # sfdisk 143 | btrfspi.config.system.build.nixos-install 144 | ]; 145 | 146 | preVM = '' 147 | ${pkgs.vmTools.qemu}/bin/qemu-img create -f raw ./btrfspi.iso 8G 148 | ''; 149 | postVM = with pkgs; '' 150 | # Truncate the file at the end of the last partition 151 | PATH=${util-linux}/bin:${jq}/bin:${zstd}/bin:$PATH 152 | img=./btrfspi.iso 153 | 154 | json=$(sfdisk --json --output end "$img") 155 | start=$(jq .partitiontable.partitions[-1].start <<< "$json") 156 | size=$(jq .partitiontable.partitions[-1].size <<< "$json") 157 | sectsize=$(jq .partitiontable.sectorsize <<< "$json") 158 | endbytes=$((("$start" + "$size" + 1) * "$sectsize")) 159 | 160 | truncate --size "$endbytes" "$img" 161 | 162 | zstd --compress "$img" 163 | 164 | mkdir -p $out 165 | mv "$img".zst $out/ 166 | ''; 167 | memSize = "4G"; 168 | QEMU_OPTS = 169 | "-drive " 170 | + builtins.concatStringsSep "," [ 171 | "file=./btrfspi.iso" 172 | "format=raw" 173 | "if=virtio" 174 | "cache=unsafe" 175 | "werror=report" 176 | ]; 177 | } '' 178 | 179 | # NB: Don't set -f, as some of the builtin nix stuff depends on globbing 180 | set -Eeu -o pipefail 181 | set -x 182 | 183 | shrinkBTRFSFs() { 184 | local mpoint shrinkBy 185 | mpoint=''${1:-/mnt} 186 | 187 | while :; do 188 | shrinkBy=$( 189 | btrfs filesystem usage -b "$mpoint" | 190 | awk \ 191 | -v fudgeFactor=0.9 \ 192 | -F'[^0-9]' \ 193 | ' 194 | /Free.*min:/ { 195 | sz = $(NF-1) * fudgeFactor 196 | print int(sz) 197 | exit 198 | } 199 | ' 200 | ) 201 | btrfs filesystem resize -"$shrinkBy" "$mpoint" || break 202 | done 203 | btrfs scrub start -B "$mpoint" 204 | } 205 | 206 | shrinkLastPartition() { 207 | local blockDev sizeInK partNum 208 | 209 | blockDev=''${1:-/dev/vda} 210 | sizeInK=$2 211 | 212 | partNum=$( 213 | lsblk --paths --list --noheadings --output name,type "$blockDev" | 214 | awk \ 215 | -v blockdev="$blockDev" \ 216 | ' 217 | # Assume lsblk has output these in order, get the name of 218 | # last device it identifies as a partition 219 | $2 == "part" { 220 | partname = $1 221 | } 222 | 223 | # Strip out the blockdev so we get just partition number 224 | END { 225 | gsub(blockdev, "", partname) 226 | print partname 227 | } 228 | ' 229 | ) 230 | 231 | echo ",$sizeInK" | sfdisk -N"$partNum" "$blockDev" 232 | ${pkgs.udev}/bin/udevadm settle 233 | } 234 | 235 | ${pkgs.kmod}/bin/modprobe btrfs 236 | ${pkgs.udev}/lib/systemd/systemd-udevd & 237 | 238 | # Gap before first partition 239 | gap=1 240 | 241 | firmwareSize=512 242 | firmwareSizeBlocks=$(( $firmwareSize * 1024 * 1024 / 512 )) 243 | 244 | # type=b is 'W95 FAT32', 83 is 'Linux'. 245 | # The "bootable" partition is where u-boot will look file for the bootloader 246 | # information (dtbs, extlinux.conf file). 247 | # Setting the bootable flag on the btrfs partition allows booting directly 248 | 249 | fatBootable= 250 | BTRFSBootable=bootable 251 | if [ ! ${toString bootFromBTRFS} ]; then 252 | fatBootable=bootable 253 | BTRFSBootable= 254 | fi 255 | 256 | sfdisk /dev/vda <> /tmp/firmware/config.txt 310 | 311 | if [ ${toString bootFromBTRFS} ]; then 312 | bootDest=/mnt/boot 313 | else 314 | bootDest=/tmp/firmware 315 | fi 316 | 317 | ${firmwarePartOpts.populateCmd} -c ${toplevel} -d "$bootDest" -g 0 318 | 319 | for config in ${toString (configFiles ./nixos)}; do 320 | install -Dm0644 -t /mnt/etc/nixos "$config"/share/* 321 | done 322 | 323 | export NIX_STATE_DIR=$TMPDIR/state 324 | nix-store < ${closure}/registration \ 325 | --load-db \ 326 | --option build-users-group "" 327 | 328 | cp ${closure}/registration /mnt/nix-path-registration 329 | 330 | echo "running nixos-install..." 331 | nixos-install \ 332 | --max-jobs auto \ 333 | --cores 0 \ 334 | --root /mnt \ 335 | --no-root-passwd \ 336 | --no-bootloader \ 337 | --substituters "" \ 338 | --option build-users-group "" \ 339 | --system ${toplevel} 340 | 341 | # Disable automatic creation of a default nix channel 342 | # See also `nix-daemon.nix` 343 | mkdir -p /mnt/root 344 | touch /mnt/root/.nix-channels 345 | 346 | # Initialize a repo to keep track of config changes and automatic flake input 347 | # updates 348 | pushd /mnt/etc/nixos 349 | git init 350 | git add . 351 | popd 352 | 353 | shrinkBTRFSFs /mnt 354 | 355 | local sizeInK 356 | sizeInK=$( 357 | btrfs filesystem usage -b /mnt | 358 | awk '/Device size:/ { print ($NF / 1024) "KiB" }' 359 | ) 360 | 361 | umount -R /mnt /tmp/firmware 362 | 363 | shrinkLastPartition /dev/vda "$sizeInK" 364 | btrfs check /dev/disk/by-label/NIXOS_SD 365 | '') 366 | --------------------------------------------------------------------------------