Architecture
Architecture
Section titled โArchitectureโUnderstanding how the Raspberry Pi Image Builder works under the hood.
Overview
Section titled โOverviewโThe build system creates hybrid images by combining:
- Raspberry Pi OS - Boot partition with firmware, bootloader, and config
- Debian ARM64 - Root filesystem with full userspace
- RaspiOS Packages - Kernel and firmware installed via APT (with auto-update support)
- Custom Services - Modular components composed during build
The result is a Debian system with full Raspberry Pi hardware support and automatic kernel/firmware updates.
Design Philosophy
Section titled โDesign PhilosophyโWhy Hybrid Images?
Section titled โWhy Hybrid Images?โProblem: Raspberry Pi uses proprietary firmware and the RP1 chip for I/O (Ethernet, USB, GPIO). Standard Debian ARM64 kernels lack these drivers.
Solution: Keep Raspberry Pi OS boot partition and kernel packages, but use Debian for everything else.
Benefits:
- Full hardware support (RP1, WiFi, Bluetooth, GPIO)
- Automatic kernel/firmware updates via
apt upgrade - Debianโs package ecosystem and stability
- No manual kernel compilation or firmware management
Why Install Packages in QEMU?
Section titled โWhy Install Packages in QEMU?โTraditional Approach:
- Merge images
- Chroot into ARM64 rootfs from x86_64 host
- Use qemu-user-static for emulation
- Install packages
Our Approach:
- Install packages in native ARM64 QEMU VM before merge
- Merge pre-configured Debian image with RaspiOS boot
Advantages:
- Simpler: No complex chroot setup
- Faster: Native ARM64 execution, no user-mode overhead
- Cleaner: Merge script just copies files, no package management
- Reproducible: Same environment every build
Build Pipeline
Section titled โBuild Pipelineโ4-Stage Process
Section titled โ4-Stage Processโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Stage 1: Download & Prepare โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โข Download RaspiOS Lite image โโ โข Download Debian cloud/generic ARM64 image โโ โข Parse service configuration โโ โข Resolve service dependencies โโ โข Combine setup scripts from all services โโ โข Create setup.iso (contains setup scripts + files) โโ โข Generate cloud-init seed.img OR inject first-boot service โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Stage 2: QEMU Setup (ARM64 VM) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โข Boot Debian ARM64 in QEMU โโ โข Cloud-init or first-boot service creates user โโ โข Mount setup.iso โโ โข Execute combined setup.sh: โโ - Add RaspiOS APT repository + pinning โโ - Install RaspiOS kernel packages (raspberrypi-kernel) โโ - Install RaspiOS firmware packages โโ - Install service packages (docker, incus, etc.) โโ - Copy configuration files to /etc/setupfiles/ โโ - Copy first-boot scripts โโ โข Auto-shutdown when complete โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Stage 3: Merge โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โข Call merge-debian-raspios.sh โโ โข Keep RaspiOS boot partition (FAT32): โโ - bootloader, config.txt, cmdline.txt โโ โข Backup RaspiOS /etc/fstab โโ โข Replace root partition with Debian (ext4): โโ - Delete RaspiOS rootfs โโ - rsync Debian rootfs (with RaspiOS packages installed) โโ - Restore RaspiOS fstab (correct partition UUIDs) โโ - Create /boot/firmware mount point โโ โข Resize root partition to fill image โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Stage 4: Compress โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โข Run PiShrink to minimize filesystem โโ โข Compress with xz (parallel, level 6) โโ โข Generate checksums (SHA256) โโ โข Output: image-name.img.xz โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโPartition Layout
Section titled โPartition LayoutโBefore Merge
Section titled โBefore MergeโRaspiOS Image:
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโ /dev/loop0p1 โ /dev/loop0p2 โโ boot (FAT32) โ root (ext4) โโ 512MB โ ~2GB โโ ================== โ ==================== โโ โข bootloader โ โข RaspiOS rootfs โโ โข kernel โ โข (will be replaced) โโ โข firmware โ โโ โข config.txt โ โโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโDebian Image:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ /dev/loop1p1 (or p2, auto-detected) โโ root (ext4) โโ ~2-4GB โโ ========================================== โโ โข Debian userspace โโ โข RaspiOS kernel packages (from QEMU) โโ โข Service packages (from QEMU) โโ โข /etc/setupfiles/ (configs) โโ โข First-boot scripts โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโAfter Merge
Section titled โAfter MergeโHybrid Image:
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ /dev/mmcblk0p1 โ /dev/mmcblk0p2 โโ boot (FAT32) โ root (ext4) โโ 512MB โ 8GB (expanded) โโ ================== โ ============================ โโ โข RaspiOS bootload โ โข Debian userspace โโ โข RaspiOS kernel* โ โข RaspiOS kernel packages โโ โข RaspiOS firmware โ โข RaspiOS firmware packages โโ โข config.txt โ โข RaspiOS fstab โโ โ โข /boot/firmware โ p1 โโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ (kept from RaspiOS) (replaced with Debian)
* Kernel files also in /boot/ and /lib/modules/ from APT packagesModular Service System
Section titled โModular Service SystemโService Directory Structure
Section titled โService Directory StructureโEach service is a self-contained module:
services/โโโ <service-name>/ โโโ setup.sh # Runs in QEMU (package installation) โโโ first-boot/ โ โโโ init.sh # Runs on first boot (runtime config) โโโ setupfiles/ # Static files โ /etc/setupfiles/ โ โโโ config.xyz โโโ depends.sh # Optional: dependencies โโโ motd.sh # Optional: MOTD contentService Lifecycle
Section titled โService LifecycleโBUILD TIME (QEMU):โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ setup.sh โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โข Install packages (apt install ...) โโ โข Configure system settings โโ โข Create users/groups โโ โข Set up repositories โโ โข Copy setupfiles/ to /etc/setupfiles/ โโ โข Install first-boot/init.sh โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
FIRST BOOT (Raspberry Pi):โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ rpi-first-boot.service (one-time) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โข Expand root partition to fill SD card โโ โข Set persistent network interface names โโ โข Reboot โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ services-first-boot.service (one-time) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โข Execute base/first-boot/init.sh โโ - Configure network bridges (br-wan, br-lan) โโ - Set up DHCP server (if br-lan) โโ โข Execute service/first-boot/init.sh โโ - Download images (HAOS, OpenWrt) โโ - Create containers/VMs โโ - Detect and configure hardware โโ - Start services โโ โข Disable itself โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
RUNTIME:โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Services running โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โข Docker containers โโ โข Incus VMs/containers โโ โข WiFi hotspot โโ โข OpenWrt router โโ โข etc. โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโService Dependency Resolution
Section titled โService Dependency ResolutionโServices can declare dependencies:
Example: services/haos/depends.sh
DEPENDS_ON="qemu"Build process:
- Parse requested services:
qemu+docker+haos - Resolve dependencies:
qemu(no dependencies)docker(no dependencies)haosโ requiresqemu
- Build order:
baseโqemuโdockerโhaos - Combine setup.sh from all services
Service Composition
Section titled โService CompositionโDynamic image example: debian/qemu+docker+haos
Process:
- Create temporary directory:
images/debian-qemu-docker-haos/ - Copy base config:
images/debian/config.sh - Override variables:
Terminal window OUTPUT_IMAGE="debian-qemu-docker-haos.img"SERVICES="base qemu docker haos" - Combine setup scripts:
Terminal window cat services/base/setup.sh \services/qemu/setup.sh \services/docker/setup.sh \services/haos/setup.sh \> setup.sh - Merge setupfiles:
Terminal window cp -r services/base/setupfiles/* setupfiles/cp -r services/qemu/setupfiles/* setupfiles/cp -r services/docker/setupfiles/* setupfiles/cp -r services/haos/setupfiles/* setupfiles/ - Inject first-boot scripts:
Terminal window # Aggregate all init.sh into services-first-boot.sh - Build image
APT Repository Management
Section titled โAPT Repository ManagementโRepository Configuration
Section titled โRepository ConfigurationโFile: /etc/apt/sources.list.d/raspi.sources
Types: debURIs: http://archive.raspberrypi.com/debian/Suites: trixieComponents: mainSigned-By: /usr/share/keyrings/raspberrypi-archive-keyring.pgpFile: /etc/apt/preferences.d/raspi-pin
# Pin RaspiOS kernel and firmware packagesPackage: raspberrypi-kernel raspberrypi-bootloader libraspberrypi* firmware-brcm80211Pin: release o=Raspberry Pi FoundationPin-Priority: 1001
# Default to Debian for everything elsePackage: *Pin: release o=DebianPin-Priority: 500Update Behavior
Section titled โUpdate Behaviorโsudo apt update# Fetches package lists from:# - Debian repositories (default)# - RaspiOS repository (for kernel/firmware)
sudo apt upgrade# Upgrades:# - raspberrypi-kernel โ from RaspiOS repo (priority 1001)# - raspberrypi-bootloader โ from RaspiOS repo (priority 1001)# - libraspberrypi* โ from RaspiOS repo (priority 1001)# - firmware-brcm80211 โ from RaspiOS repo (priority 1001)# - All other packages โ from Debian repos (priority 500)Result: Kernel and firmware stay in sync with RaspiOS, userspace packages track Debian.
Boot Modes
Section titled โBoot ModesโCloud-Init Mode (CLOUD=true)
Section titled โCloud-Init Mode (CLOUD=true)โUse when:
- Using Debian cloud images
- Need cloud-init features (network config, metadata, etc.)
Files:
cloudinit/user-data- User creation, SSH config, runcmdcloudinit/meta-data- Instance ID, hostnamecloudinit/seed.img- Auto-generated ISO (CIDATA volume)
Boot process:
- QEMU mounts seed.img (cloud-init config)
- QEMU mounts setup.iso (build scripts)
- Cloud-init creates user and runs runcmd
- runcmd mounts setup.iso and executes setup.sh
- VM shuts down after setup
First-Boot Service Mode (CLOUD=false)
Section titled โFirst-Boot Service Mode (CLOUD=false)โUse when:
- Using generic Debian images
- Donโt need cloud-init
- Want minimal dependencies
Files:
first-boot/setup-runner.sh- Creates user, runs setupfirst-boot/setup-runner.service- Systemd one-shot service
Boot process:
- Autobuild injects files into Debian image (before QEMU)
- Autobuild enables systemd service via chroot
- QEMU boots Debian
- Systemd starts setup-runner.service
- Service creates user, mounts setup.iso, runs setup.sh
- Service disables itself
- VM shuts down after setup
Hardware Detection
Section titled โHardware DetectionโDetection Points
Section titled โDetection Pointsโ1. Build Time (QEMU):
- Install packages needed for hardware detection
- Copy detection scripts to /etc/setupfiles/
2. First Boot (Raspberry Pi):
- Detect network interfaces (eth0, eth1)
- Detect WiFi adapters (wlan0, wlan1)
- Detect USB Zigbee dongles
- Configure services based on detected hardware
Network Interface Detection
Section titled โNetwork Interface DetectionโFile: services/base/first-boot/init.sh
# Detect eth1 (second NIC)if ip link show eth1 >/dev/null 2>&1; then # Dual NIC mode # br-wan (eth0) - WAN with DHCP client # br-lan (eth1) - LAN with DHCP server + NATelse # Single NIC mode # br-wan (eth0) - WAN with DHCP clientfiResult:
- Single NIC: WAN only
- Dual NIC: WAN + LAN with DHCP/NAT
WiFi Detection
Section titled โWiFi DetectionโFile: services/hotspot/first-boot/init.sh
# Detect WiFi interfaceswlan0_exists=$(ip link show wlan0 2>/dev/null)wlan1_exists=$(ip link show wlan1 2>/dev/null)
if [ -n "$wlan0_exists" && -n "$wlan1_exists" ](/raspberry-builds/docs/--n-"$wlan0_exists"-&&--n-"$wlan1_exists"-); then # Dual-band: 2.4GHz on wlan0, 5GHz on wlan1elif [ -n "$wlan0_exists" ](/raspberry-builds/docs/--n-"$wlan0_exists"-); then # Single-band: 5GHz on wlan0fi
# Determine bridgeif ip link show br-lan >/dev/null 2>&1; then BRIDGE="br-lan"else BRIDGE="br-wan"fiResult:
- Dual WiFi: 2.4GHz + 5GHz APs
- Single WiFi: 5GHz AP only
- Adapts to available bridge
USB Zigbee Detection
Section titled โUSB Zigbee DetectionโFile: services/haos/first-boot/init.sh
# Scan USB serial devicesfor device in /dev/ttyUSB* /dev/ttyACM*; do device_info=$(udevadm info -q property -n "$device")
# Check vendor for known Zigbee coordinators if echo "$device_info" | grep -qiE "(FTDI|Silicon_Labs|Texas_Instruments|dresden_elektronik|ITead|Sonoff)"; then # Extract USB IDs USB_VENDOR=$(echo "$device_info" | grep "ID_VENDOR_ID=" | cut -d'=' -f2) USB_PRODUCT=$(echo "$device_info" | grep "ID_MODEL_ID=" | cut -d'=' -f2)
# Pass through to Home Assistant VM incus config device add haos zigbee-dongle usb \ vendorid="$USB_VENDOR" \ productid="$USB_PRODUCT" \ required=false fidoneResult: Zigbee coordinators automatically available in Home Assistant.
Image Size Management
Section titled โImage Size ManagementโSize Calculation
Section titled โSize CalculationโAutobuild logic:
# Debian image sizeDEBIAN_SIZE=$(qemu-img info --output=json debian.raw | jq -r '.["virtual-size"]')
# Add overhead (1-2GB for services, temp files, expansion)FINAL_SIZE=$((DEBIAN_SIZE + 2GB))
# Override with config.sh IMAGE_SIZE if specifiedPartition Expansion
Section titled โPartition ExpansionโDuring merge (merge-debian-raspios.sh):
- Create output image with
IMAGE_SIZE - Resize partition table
- Expand root partition to fill available space
- Resize ext4 filesystem
On first boot (rpi-first-boot.sh):
- Expand root partition to fill SD card
- Resize ext4 filesystem to match
- Reboot to apply changes
Result: Image expands to fill entire SD card, regardless of size.
Summary
Section titled โSummaryโThe architecture uses:
- Hybrid approach - RaspiOS boot + Debian rootfs
- QEMU ARM64 - Native package installation before merge
- Modular services - Composable image components
- APT pinning - Automatic kernel/firmware updates
- Hardware detection - Runtime configuration based on detected hardware
- Two boot modes - Cloud-init or first-boot service
This design provides:
- Full Raspberry Pi hardware support
- Safe automatic updates
- Easy customization
- Reproducible builds
- Team collaboration via version control