The ThinkPad x220 that I had been using as an ssh terminal at home finally developed one too many hardware problems a few weeks ago, and so I ordered a Raspberry Pi 4b to replace it. Debian builds minimal SD card images for these machines already, but I wanted to use the usual ext4-on-LVM-on-LUKS setup for GNU/Linux workstations. So I used Consfigurator to build a custom image.

There are two key advantages to using Consfigurator to do something like this:

  1. As shown below, it doesn’t take a lot of code to define the host, it’s easily customisable without writing shell scripts, and it’s all declarative. (It’s quite a bit less code than Debian’s image-building scripts, though I haven’t carefully compared, and they are doing some additional setup beyond what’s shown below.)

  2. You can do nested block devices, as required for ext4-on-LVM-on-LUKS, without writing an intensely complex shell script to expand the root filesystem to fill the whole SD card on first boot. This is because Consfigurator can just as easily partition and install an actual SD card as it can write out a disk image, using the same host definition.

Consfigurator already had all the capabilities to do this, but as part of this project I did have to come up with the high-level wrapping API, which didn’t exist yet. My first SD card write wouldn’t boot because I had to learn more about kernel command lines; the second wouldn’t boot because of a minor bug in Consfigurator regarding /etc/crypttab; and the third build is the one I’m using, except that the first boot runs into a bug in cryptsetup-initramfs. So as far as Consfigurator is concerned I would like to claim that it worked on my second attempt, and had I not been using LUKS it would have worked on the first :)

The code

(defhost erebus.silentflame.com ()
  "Low powered home workstation in Tucson."
  (os:debian-stable "bullseye" :arm64)
  (timezone:configured "America/Phoenix")

  (user:has-account "spwhitton")
  (user:has-enabled-password "spwhitton")

  (disk:has-volumes
   (physical-disk
    (partitioned-volume

     ((partition
       :partition-typecode #x0700 :partition-bootable t :volume-size 512
       (fat32-filesystem :mount-point #P"/boot/firmware/"))

      (partition
       :volume-size :remaining

       (luks-container
        :volume-label "erebus_crypt"
        :cryptsetup-options '("--cipher" "xchacha20,aes-adiantum-plain64")

        (lvm-physical-volume :volume-group "vg_erebus"))))))

   (lvm-logical-volume
    :volume-group "vg_erebus"
    :volume-label "lv_erebus_root" :volume-size :remaining

    (ext4-filesystem :volume-label "erebus_root" :mount-point #P"/"
                     :mount-options '("noatime" "commit=120"))))

  (apt:installed "linux-image-arm64" "initramfs-tools"
                 "raspi-firmware" "firmware-brcm80211"
                 "cryptsetup" "cryptsetup-initramfs" "lvm2")
  (etc-default:contains "raspi-firmware"
                        "ROOTPART" "/dev/mapper/vg_erebus-lv_erebus_root"
                        "CONSOLES" "ttyS1,115200 tty0"))

and then you just insert the SD card and, at the REPL on your laptop,

CONSFIG> (hostdeploy-these laptop.example.com
           (disk:first-disk-installed-for nil erebus.silentflame.com #P"/dev/mmcblk0"))

There is more general information in the OS installation tutorial in the Consfigurator user’s manual.

Other niceties

  • Configuration management that’s just as easily applicable to OS installation as it is to the more usual configuration of hosts over SSH drastically improves the ratio of cost-to-benefit for including small customisations one is used to.

    For example, my standard Debian system configuration properties (omitted from the code above) meant that when I was dropped into an initramfs shell during my attempts to make an image that could boot itself, I found myself availed of my custom Space Cadet-inspired keyboard layout, without really having thought at any point “let’s do something to ensure I can have my usual layout while I’m figuring this out.” It was just included along with everything else.

  • As compared with the ThinkPad x220, it’s nice how the Raspberry Pi 4b is silent and doesn’t have any LEDs lit by default once it’s booted. A quirk of my room is that one plug socket is controlled by a switch right next to the switch for the ceiling light, so I’ve plugged my monitor into that outlet. Then when I’ve finished using the new machine I can flick that switch and the desk becomes completely silent and dark, without actually having to suspend the machine to RAM, thereby stopping cron jobs, preventing remote access from the office to fetch uncommitted files, etc..