Create a preconfigured Raspberry Pi OS image (Pi 5, Bookworm arm64) with the Pinewood Robotics B.L.I.T.Z stack and Autobahn preinstalled. The build runs entirely inside Docker using QEMU, expands the base image, chroots into it to install packages and services, and then exports a compressed .img.xz ready to flash.
- Downloads Raspberry Pi OS Lite (arm64 Bookworm) base image
- Expands the image filesystem to add space (+4 GB by default)
- Chroots into the image using QEMU to install software
- Installs common system deps, Rust toolchain, SSH, mDNS
- Installs B.L.I.T.Z (branch
merge-backend) and Autobahn - Applies USB udev naming rules and enables required services
- Configures a first-boot service that asks for a device name
- Exports and compresses the final image to
outputs/
- Docker Engine (or Docker Desktop) and Docker Compose
- Host OS: Linux recommended; macOS with Docker Desktop also works (runs in a Linux VM under the hood). Windows WSL2 with Docker may work as well.
- Sufficient disk space (downloaded image + expanded working set; plan for ~10–15 GB)
- Internet access (clones/installs inside the chroot)
git clone https://github.com/your-org/BlitzImageModifier.git
cd BlitzImageModifier
# Build and run the image creation pipeline
docker compose up --buildWhen the pipeline finishes, your output will be in:
outputs/pi5_flash_image.img.xzYou can then flash it to a microSD card (Linux example):
xz -d outputs/pi5_flash_image.img.xz
# Replace /dev/sdX with your target device (DANGER: double-check!)
sudo dd if=outputs/pi5_flash_image.img of=/dev/sdX bs=4M status=progress conv=fsync-
Dockerfile- Based on
ubuntu:24.04, installs tooling (qemu-user-static, kpartx, parted, e2fsprogs, etc.) - Downloads
raspios-bookworm-arm64-liteand expands the image by 4 GB - Entrypoint runs
setup_image.bashthenexport_image_and_compress.bash
- Based on
-
setup_image.bash- Attaches loop devices, fixes/extends partitions, runs
resize2fs - Mounts the image (root and boot), binds
/dev,/proc,/sys,/workspace - Copies
qemu-aarch64-staticinto the chroot for ARM emulation - Chroots into the image and runs
main_startup.bash pi5.bash
- Attaches loop devices, fixes/extends partitions, runs
-
Inside the chroot (
main_startup.bash)- Runs hardware script
pi5.bash(installs udev rule) - Runs
installation_common.bash(packages, Rust, SSH/mDNS enable) - Runs
installation_blitz.bash(clones B.L.I.T.Z,scripts/install.bashwith default name) - Runs
installation_autobahn.bash(clones Autobahn, installs) - Runs
post_install.bash(installs and enables first-boot service)
- Runs hardware script
-
export_image_and_compress.bash- Cleanly unmounts/breaks down loop/mapper devices
- Renames the image, compresses to
.img.xz, and copies into./outputs/
- A systemd service (
blitz_project.service) runs/usr/local/bin/blitzprojstartup.bash. - On first boot, if the default name is still set, it will prompt on the console for a new device name and apply it (
hostnamectl,/etc/hosts, restart Avahi/SSH), then reboot.- If you need a non-interactive first boot, pre-create
name.txtinside the image at:/opt/blitz/B.L.I.T.Z/system_data/name.txt
- If you need a non-interactive first boot, pre-create
- Base image version: in
Dockerfile(wgetURL). Update to a newer Raspberry Pi OS image if desired. - Extra space: in
Dockerfile(truncate -s +4G ...). Increase if you need more room preinstalled. - Hardware script: currently
main_startup.bashrunspi5.bash. Add your own script and change the argument insetup_image.bash(main_startup.bash <your-script>.bash). - B.L.I.T.Z branch: in
installation_blitz.bash(BRANCH_NAME="merge-backend"). Change as needed. - Default device name:
installation_blitz.bash(DEFAULT_PI_NAME). This is used on first boot before prompting. - udev rules: edit
installation/system-patch/90-usb-port-names.rulesor adaptpi5.bashto your hardware. - Output filename:
export_image_and_compress.bashis invoked withpi5_flash_imageby default (fromDockerfile CMD). Change it if you want a different name.
Dockerfile: Builder and orchestrator for the image creationcompose.yml: Docker Compose service (privileged) that runs the pipelinesetup_image.bash: Mounts/extends the downloaded image and enters chrootmain_startup.bash: Orchestrates installs and setup inside the chrootpi5.bash: Installs udev rule for USB port naminginstallation_common.bash: Common packages, Rust toolchain, SSH/mDNS enableinstallation_blitz.bash: Clones/installs B.L.I.T.Zinstallation_autobahn.bash: Clones/installs Autobahnpost_install.bash: Installs and enables first-boot naming serviceblitz_project.service: systemd unit for first-boot namingblitzprojstartup.bash: prompts for device name on first bootexport_image_and_compress.bash: unmounts, compresses, and copies outputoutputs/: final.img.xzartifacts
- The build needs a privileged container for loop/mapper devices. Ensure Compose runs with
privileged: true(already incompose.yml). - On macOS/Windows, Docker runs inside a Linux VM; the pipeline runs in that VM and should still work, but performance can be slower.
- If the build fails while chrooting, verify that
qemu-user-staticwas copied andbinfmtis active (handled by the Dockerfile). - If the output file is missing, check container logs for unmount/cleanup issues near the end.
- If first-boot name prompt is undesirable (e.g., headless), pre-seed
name.txtas noted above.
# Build and run
docker compose up --build
# Clean Compose state (if you re-run)
docker compose down -v
# Remove old outputs (optional)
rm -f outputs/*.img outputs/*.img.xz- Port
5555is exposed incompose.ymlfor possible stack services; adjust as needed. - SSH and Avahi (mDNS) are enabled in the image so it is discoverable on the network as soon as it boots.
Add a license here (e.g., MIT, Apache-2.0) if applicable.