OctoPi#

Role#

Raspberry Pi 3B in the kitchen/workshop corner. Tagged rpi in hosts/default.nix (aarch64 — deploy pushes closures; nothing builds on-device).

The config path is misleading

octopi lives at hosts/x86_64-nixos/octopi/ although it is aarch64. The path is historical; arch, class, and system come from its entry in hosts/default.nix, not from the directory name (haxo, the other Pi, sits under hosts/aarch64-nixos/). Moving it is a one-line registry edit that needs an rpi cache-hit check afterwards — tracked in In-flight work.

What it runs#

  • OctoPrint (services.octoprint) — the 3D-printer controller.
  • Kitchen barcode scanner (tsunaminoai.barcodeScanner) — a Tera HW0009 wireless scanner bridged evdev → MQTT for Home Assistant / grocery intake.
  • Mail document scanner (tsunaminoai.documentScanner) — the ScanSnap S1500M’s hardware button scans multipage PDFs straight into Paperless on ereshkigal.
  • clamav is force-disabled (tsunaminoai.security.clamav.enable = false) — clamd’s resident signature database OOM-killed the 1 GB / no-swap box and starved sshd. (It was not the cause of the Wi-Fi failure; see below.)

Fast facts#

          ▗▄▄▄       ▗▄▄▄▄    ▄▄▄▖             tsunami@octopi
          ▜███▙       ▜███▙  ▟███▛             --------------
           ▜███▙       ▜███▙▟███▛              OS: NixOS 24.11.20250106.3f0a8ac (Vicuna) aarch64
            ▜███▙       ▜██████▛               Host: Raspberry Pi 3 Model B Rev 1.2
     ▟█████████████████▙ ▜████▛     ▟▙         Kernel: Linux 6.6.31
    ▟███████████████████▙ ▜███▙    ▟██▙        Uptime: 3 mins
           ▄▄▄▄▖           ▜███▙  ▟███▛        Packages: 879 (nix-system)
          ▟███▛             ▜██▛ ▟███▛         Shell: fish 3.7.1
         ▟███▛               ▜▛ ▟███▛          Display (Unknown-1): 656x416
▟███████████▛                  ▟██████████▙    Terminal: /dev/pts/0
▜██████████▛                  ▟███████████▛    CPU: BCM2837 (4) @ 1.20 GHz
      ▟███▛ ▟▙               ▟███▛             Memory: 281.50 MiB / 893.14 MiB (32%)
     ▟███▛ ▟██▙             ▟███▛              Swap: Disabled
    ▟███▛  ▜███▙           ▝▀▀▀▀               Disk (/): 9.51 GiB / 14.47 GiB (66%) - ext4
    ▜██▛    ▜███▙ ▜██████████████████▛         Local IP (wlan0): 192.168.0.163/24
     ▜▛     ▟████▙ ▜████████████████▛          Locale: en_US.UTF-8
           ▟██████▙       ▜███▙
          ▟███▛▜███▙       ▜███▙
         ▟███▛  ▜███▙       ▜███▙
         ▝▀▀▀    ▀▀▀▀▘       ▀▀▀▘

Snapshot age

The capture above is from 2025-02-04 (the old nixos-hardware raspberry-pi-4 image). octopi was reflashed on 2026-06-26 onto the nixos-raspberrypi rpi-3 layout with the cached linux-rpi kernel; treat the numbers above as historical colour.

Physical constraints#

  • Raspberry Pi 3 Model B Rev 1.2, 1 GB RAM, no swap, 2.4 GHz-only BCM43430 Wi-Fi on brcmfmac.
  • Built-in Ethernet is USB-attached and enumerates as enu1u1 (not end0/eth0). networking.interfaces is force-cleared so systemd does not wait 90 s for the eno1 the shared core module declares.
  • Power it from a real 5 V / 2.5–3 A supply, never a dock or hub USB port. Undervoltage produces an escalating ladder that looks like a driver bug: first Wi-Fi associates but the board hangs within ~20 s under transmit, then wlan0 stops appearing at boot with brcmf_sdio_verifymemory: Downloaded RAM image is corrupted. That is a brownout signature (hwmon: Undervoltage detected! in dmesg), not firmware.
  • ClamAV stays off (above). Docs, man pages, and glances are off for the same reason on both Pis.

Wi-Fi: the real root causes#

The long-running “wlan0 will not associate (status_code=16)” was not memory starvation (it failed with 700 MiB free). Two fixes, both in hosts/x86_64-nixos/octopi/default.nix:

  1. NixOS injects a global pmf=1 into wpa_supplicant.conf; this Pi’s brcmfmac FullMAC firmware mishandles PMF and every association dies at Connect event (status=16). A per-network ieee80211w=0 overrides it (the AP advertises no MFP, so nothing is lost). WPA2-Personal is forced because WPA3/SAE never associates on this chip.
  2. wpa_supplicant-wlan0.service runs as the unprivileged wpa_supplicant user (nixpkgs hardening), so the sops wifi secret must be owner = "wpa_supplicant" or the PSK file is unreadable and there is no key to associate with.

haxo carries the same two workarounds.

Deploying changes#

  • Incremental changes go over the network, never by reflash: nh os switch . -H octopi --target-host tsunami@<lan-ip> (use the LAN address; the Tailscale name is unreliable right after a reflash).
  • Reflash only for bootloader or partition changes. switch-to-configuration cannot repartition an SD card. The nixos-raspberrypi layout uses a separate FAT /boot/firmware partition plus u-boot; switching across that boundary fails on a missing /boot/firmware, and if you mkdir it by hand u-boot writes firmware onto the ext4 root — which the Pi GPU never reads at power-on. The board comes up with no ping. That is how octopi was bricked once (2026-06).
  • Recovery: pull the SD card, just flash-octopi (decrypts the host key from sops, builds the image with the nixos-raspberrypi.cachix.org substituter so the kernel is a cache hit, flashes it), reinsert, boot.
  • After any reflash, Tailscale needs a fresh auth key. A flash wipes tailscale state; tailscale-autoconnect re-registers with the sops tailscale/auth-key, and a stale key fails with invalid key: API key does not exist, leaving the node “Logged out” — which looks exactly like the Pi never came back if you only reach it by its tailnet name. Mint a reusable, tagged key in the Tailscale admin console, sops set it, rebuild. One-time fallback over the LAN: sudo tailscale up --ssh. Tailnet Lock then also needs tailscale lock sign from mokou (Adding a Host).
  • Do not “fix” normaliseClass blind. modules/flake/nix/default.nix never maps rpi → nixos, so octopi lacks modules/flake/nix/nixos.nix (experimental-features, substituters, nh, distributed builds). The host sets nix.settings.experimental-features itself as a workaround. Repairing the lookup changes octopi’s closure — verify the rpi kernel still substitutes (nix build --dry-run …octopi…toplevel shows nothing linux-rpi under “will be built”) before flashing.
  • Paperless — where the mail scanner’s PDFs land.
  • Haxo — the other Pi in the fleet.