#### ##### ### #### #### #####
# # # # # # # # # #
#### #### ##### #### #### #
# # # # # # # #
# ##### # # # # # #####
Buildroot BR2_EXTERNAL tree for pearpi, a custom Raspberry Pi 4 (64-bit) image:
busybox init (no systemd), no Node.js, UART0 enabled on GPIO14/15
(dtoverlay=disable-bt instead of miniuart-bt), 190M rootfs that grows
to fill whatever SD card it's flashed onto, a pear binary dropped into
/usr/bin via a rootfs overlay, and SSH (dropbear) locked to
pubkey-only login for root.
This tree contains no Buildroot source itself — it's meant to be built against an unmodified Buildroot checkout, pinned to a known version.
- A Buildroot checkout at tag
2026.05.1(or compatible), untouched/pristine. - The
pearbinary, copied in manually (not committed — see below).
-
Copy the
pearbinary into the overlay (not tracked in git, ~113MB):cp /path/to/dump/by-arch/linux-arm64/app/pear \ board/raspberrypi4-64/rootfs_overlay/usr/bin/pear chmod 755 board/raspberrypi4-64/rootfs_overlay/usr/bin/pear -
From your Buildroot checkout, configure the build against this external tree:
cd /path/to/buildroot make BR2_EXTERNAL=/path/to/pearpi pearpi_defconfigBR2_EXTERNALonly needs to be passed this once — Buildroot records the path inoutput/.br2-external.mkand picks it up automatically on everymakeafter that, for that same output directory.
make
Images land in output/images/ (sdcard.img is the one to dd to an SD
card/USB device).
- Changed
board/raspberrypi4-64/config_4_64bit.txt,post-build.sh, orpost-image.sh: justmakeagain — these run late in the build. - Changed
rootfs_overlay/: justmakeagain — overlays are re-applied on every build, not gated behind package rebuilds. - Changed the init system, toolchain, or any package selection
(
make menuconfig): requiresmake clean && makeafterward, since Buildroot doesn't prune stale files from a previous package selection on an incremental build (BR2_PER_PACKAGE_DIRECTORIESis off).
After changing anything in make menuconfig, capture the new deltas back
into this tree:
make savedefconfig
This writes to configs/pearpi_defconfig (per BR2_DEFCONFIG in the
build's .config) — commit that file afterward.
external.desc # declares this tree as BR2_EXTERNAL_PEARPI_PATH
configs/pearpi_defconfig # saved config deltas from Buildroot defaults
board/raspberrypi4-64/
config_4_64bit.txt # RPi firmware config.txt (UART enabled)
post-build.sh # tty1 getty setup
post-image.sh # image generation step
rootfs_overlay/
usr/bin/pear # gitignored, copy in manually (see above)
etc/default/dropbear # DROPBEAR_ARGS="-s" - disables password login
etc/fstab # adds a /boot (vfat) entry for manual mounting
etc/init.d/S02growrootfs # grows partition 2 + ext4 to fill the disk, every boot
etc/init.d/S05sshkeys # installs authorized_keys from /boot at every boot
Password login is disabled (DROPBEAR_ARGS="-s" in
rootfs_overlay/etc/default/dropbear) — pubkey is the only way in, and
no key is baked into the image at build time. Instead, whoever
flashes the SD card authorizes their own key after flashing, by
writing to the boot partition — no rebuild required:
- Flash
sdcard.imgas usual. - Before (or after) first boot, mount just the first partition (FAT,
boot) on any OS — Windows/macOS/Linux all mount FAT natively — and place a file there namedauthorized_keys, containing the public key(s) to authorize (standardauthorized_keysformat, one per line). - Boot (or reboot) the Pi.
S05sshkeysruns early on every boot, mounts/dev/mmcblk0p1read-only at/boot, and if/boot/authorized_keysexists, installs it as/root/.ssh/authorized_keys. -
ssh root@<device-ip>
Until that file is present, SSH is unreachable by design — that's the
"secure by default" property: no key, no access, and no password
fallback. Rotating keys later is just editing the same file on the boot
partition and rebooting; S05sshkeys reinstalls it every boot.
Password login is also disabled on the local console, not just over
SSH: BR2_TARGET_ENABLE_ROOT_LOGIN is off, which sets root's
/etc/shadow entry to * (an unmatchable hash) at build time. Without
this, dropbear -s only blocks SSH password auth — the getty
prompt on tty1 (HDMI) and ttyAMA0 (serial) would still accept a
password. Since there's no password at all now, physical access to the
console gets you a login prompt you can't pass — the authorized_keys
file on the boot partition is the only way into the device, full stop.
No plaintext password is ever set or gets baked into the saved
defconfig either.
The built image has a fixed-size rootfs partition (BR2_TARGET_ROOTFS_EXT2_SIZE=190M),
but SD cards it gets flashed onto are almost always much bigger.
S02growrootfs runs on every boot, before other services, and:
parted -s -f /dev/mmcblk0 resizepart 2 100%— grows partition 2 to claim all remaining space on the disk. Safe on the mounted root partition since it's the last partition and only the end boundary moves (no data is touched); the kernel is notified of the new size live, no reboot needed.resize2fs /dev/mmcblk0p2— grows the ext4 filesystem to fill the now-larger partition. ext4 supports this online (while mounted rw).
Both steps are no-ops once the partition/filesystem already fill the
disk, so it's safe and cheap to run unconditionally every boot rather
than tracking whether it already ran once. Needs parted and
e2fsprogs's resize2fs, both selected in pearpi_defconfig.