Build System
Build System
Section titled “Build System”Complete reference for the autobuild system and image creation.
Autobuild Command
Section titled “Autobuild Command”The autobuild script orchestrates the entire build process.
Basic Usage
Section titled “Basic Usage”./bin/autobuild --image <image-name>Image Formats
Section titled “Image Formats”Physical directory format:
./bin/autobuild --image debian# Uses images/debian/config.shDynamic service composition:
./bin/autobuild --image debian/qemu+docker+haos# Uses images/debian/config.sh + combines servicesCommon Options
Section titled “Common Options”# Build specific image./bin/autobuild --image debian
# Build all images from .github/images.txt./bin/autobuild --all-images
# List available images./bin/autobuild --list-images
# Skip base image downloads (use cached)./bin/autobuild --image debian --skip-download
# Skip QEMU setup (use existing Debian image)./bin/autobuild --image debian --skip-qemu
# Skip compression./bin/autobuild --image debian --skip-compress
# Clean previous builds./bin/autobuild --image debian --cleanCombined Options
Section titled “Combined Options”# Quick rebuild without downloads or compression./bin/autobuild --image debian --skip-download --skip-compress
# Rebuild with new setup scripts, skip downloads./bin/autobuild --image debian --skip-download --cleanBuild Stages
Section titled “Build Stages”Stage 1: Download & Prepare
Section titled “Stage 1: Download & Prepare”Actions:
- Download RaspiOS Lite image (if not cached)
- Download Debian ARM64 image (if not cached)
- Parse image configuration (config.sh)
- Resolve service dependencies
- Combine setup.sh from all services
- Merge setupfiles/ directories
- Create setup.iso with combined configuration
- Generate cloud-init seed.img OR inject first-boot service
Outputs:
raspios-lite.img(in distro directory)debian-arm64.raw(in distro directory)setup.iso(in image directory)seed.img(in cloudinit/ directory, if CLOUD=true)
Environment Variables:
RASPIOS_URL="https://downloads.raspberrypi.org/..."IMAGE_URL="https://cloud.debian.org/..." # from config.shCLOUD=true # or false, from config.shSERVICES="base qemu docker" # from config.sh or dynamicStage 2: QEMU Setup
Section titled “Stage 2: QEMU Setup”Actions:
- Convert Debian image to raw format (if qcow2)
- Launch QEMU ARM64 VM:
- RAM: QEMU_RAM (from config.sh)
- CPUs: QEMU_CPUS (from config.sh)
- Disk: Debian image
- CD-ROM 1: seed.img (if cloud-init mode)
- CD-ROM 2: setup.iso
- Wait for setup completion (VM auto-shutdown)
- Monitor progress via QEMU serial console
QEMU Configuration:
qemu-system-aarch64 \ -machine virt \ -cpu cortex-a72 \ -m $QEMU_RAM \ -smp $QEMU_CPUS \ -drive file=debian.raw,format=raw,if=virtio \ -drive file=seed.img,format=raw,if=virtio,readonly=on \ # cloud-init -drive file=setup.iso,format=raw,if=virtio,readonly=on \ -bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd \ -nographic \ -netdev user,id=net0 \ -device virtio-net-pci,netdev=net0Inside QEMU:
- Cloud-init or first-boot service creates user
- Setup script mounts setup.iso
- Executes combined setup.sh:
- Adds RaspiOS repository
- Installs RaspiOS kernel/firmware
- Installs service packages
- Copies setupfiles to /etc/setupfiles/
- Installs first-boot scripts
- Shuts down VM
Timeout: 30 minutes (configurable via QEMU_TIMEOUT)
Stage 3: Merge
Section titled “Stage 3: Merge”Actions:
- Call
merge-debian-raspios.sh - Keep RaspiOS boot partition (FAT32)
- Replace RaspiOS root with Debian root
- Restore RaspiOS fstab (for correct partition UUIDs)
- Resize root partition to IMAGE_SIZE
Merge Process (10 sub-stages):
- Dependency verification
- RaspiOS preparation (decompress if .xz)
- Debian preparation (convert to raw if needed)
- Size analysis
- Output image creation (copy of RaspiOS)
- Image resizing
- Loop device mounting
- Backup RaspiOS fstab
- Rootfs replacement:
- Delete RaspiOS root
- Rsync Debian root with
-aAXv(preserve all attributes) - Create
/boot/firmwaremount point - Restore fstab
- Cleanup
Output: <image-name>.img
Stage 4: Compress
Section titled “Stage 4: Compress”Actions:
- Run PiShrink to minimize filesystem
- Compress with xz (parallel, level 6)
- Generate SHA256 checksum
PiShrink:
pishrink.sh -z <image>.img <image>.img.xz# -z: Compress with xz after shrinkingOutput: <image-name>.img.xz
Configuration Files
Section titled “Configuration Files”Image Configuration (config.sh)
Section titled “Image Configuration (config.sh)”Location: images/<distro>/config.sh or images/<image-name>/config.sh
Required Variables:
# Output filenameOUTPUT_IMAGE="debian-base.img"
# Final image size (supports K, M, G suffixes)IMAGE_SIZE="8G"
# QEMU resourcesQEMU_RAM="8G"QEMU_CPUS="4"
# Boot mode (true = cloud-init, false = first-boot service)CLOUD=true
# Base distribution image URLIMAGE_URL="https://cloud.debian.org/images/cloud/trixie-backports/daily/latest/debian-13-backports-genericcloud-arm64-daily.raw"
# Services to include (space-separated)SERVICES="base qemu docker"
# Description (optional, for documentation)DESCRIPTION="Debian with Incus and Docker"Optional Variables:
# Custom RaspiOS URL (default: RaspiOS Lite trixie)RASPIOS_URL="https://downloads.raspberrypi.org/..."
# QEMU timeout in seconds (default: 1800 = 30 minutes)QEMU_TIMEOUT=3600
# Skip PiShrink compressionSKIP_PISHRINK=falseService Configuration
Section titled “Service Configuration”Each service directory contains:
setup.sh (required):
#!/bin/bashset -e
# Install packagesapt updateapt install -y package1 package2
# Configure systemsystemctl enable service1first-boot/init.sh (optional):
#!/bin/bashset -e
# Runtime configuration# Detect hardware, create containers, etc.depends.sh (optional):
# Declare dependenciesDEPENDS_ON="qemu"motd.sh (optional):
# MOTD contentcat <<'EOF'Service UI: https://raspberry-ip:9000Username: adminEOFsetupfiles/ (optional):
- Static configuration files
- Copied to /etc/setupfiles/ during build
Build Artifacts
Section titled “Build Artifacts”Directory Structure
Section titled “Directory Structure”images/└── debian/ # Distribution directory ├── config.sh # Base configuration ├── cloudinit/ # Cloud-init mode │ ├── user-data │ ├── meta-data │ └── seed.img # Auto-generated ├── services/ # Service modules │ ├── base/ │ ├── qemu/ │ └── docker/ ├── raspios-lite.img # Downloaded RaspiOS (cached) ├── debian-arm64.raw # Downloaded Debian (cached) └── debian-qemu-docker/ # Dynamic image (created during build) ├── config.sh # Generated from base + overrides ├── setup.sh # Combined from services ├── setup.iso # Generated ├── setupfiles/ # Merged from services ├── debian-qemu-docker.img # Final image └── debian-qemu-docker.img.xz # CompressedArtifact Persistence
Section titled “Artifact Persistence”Persistent (cached between builds):
raspios-lite.img- RaspiOS base imagedebian-arm64.raw- Debian base image (modified by QEMU)- Final
.imgand.img.xzfiles
Temporary (cleaned with --clean):
- Dynamic image directories (e.g.,
debian-qemu-docker/) setup.isoseed.img(regenerated each build)
Skip Downloads:
# Use cached base images./bin/autobuild --image debian --skip-downloadSkip QEMU:
# Use existing configured Debian image (skip setup in QEMU)./bin/autobuild --image debian --skip-qemuService Dependency Resolution
Section titled “Service Dependency Resolution”Dependency Declaration
Section titled “Dependency Declaration”Example: Home Assistant requires Incus
File: images/debian/services/haos/depends.sh
DEPENDS_ON="qemu"Resolution Algorithm
Section titled “Resolution Algorithm”Input: debian/qemu+docker+haos
Process:
- Parse services:
qemu,docker,haos - Resolve dependencies:
qemu: no dependenciesdocker: no dependencieshaos: depends onqemu(already in list)
- Remove duplicates
- Order by dependencies:
base→qemu→docker→haos
Output: SERVICES="base qemu docker haos"
Build Order
Section titled “Build Order”Services are built in dependency order:
- base (always first)
- Dependencies (e.g.,
qemuforhaos) - Requested services
Setup script combination:
{ cat services/base/setup.sh cat services/qemu/setup.sh cat services/docker/setup.sh cat services/haos/setup.sh} > combined-setup.shMerge Process Details
Section titled “Merge Process Details”Partition Operations
Section titled “Partition Operations”1. RaspiOS Boot Partition (kept):
# Mount as read-only (no changes)mount -o ro /dev/loop0p1 /mnt/raspios-boot2. RaspiOS Root Partition (backed up, then replaced):
# Backup fstab onlycp /mnt/raspios-root/etc/fstab /tmp/raspios-fstab
# Delete entire rootfsrm -rf /mnt/raspios-root/*3. Debian Root Partition (source):
# Rsync to RaspiOS rootrsync -aAXv /mnt/debian-root/ /mnt/raspios-root/
# Preserve attributes:# -a: archive mode (recursive, preserve permissions, times, symlinks)# -A: preserve ACLs# -X: preserve extended attributes# -v: verbose4. Restore RaspiOS fstab:
# RaspiOS fstab has correct partition UUIDscp /tmp/raspios-fstab /mnt/raspios-root/etc/fstab5. Create boot mount point:
# Ensure /boot/firmware exists for boot partitionmkdir -p /mnt/raspios-root/boot/firmwareSize Calculation
Section titled “Size Calculation”Automatic sizing:
# Get Debian image virtual sizeDEBIAN_SIZE=$(qemu-img info --output=json debian.raw | jq -r '.["virtual-size"]')
# Add overhead (1-2GB)AUTO_SIZE=$((DEBIAN_SIZE + 2 * 1024 * 1024 * 1024))
# Use IMAGE_SIZE from config if largerFINAL_SIZE=$(max $AUTO_SIZE $IMAGE_SIZE)Manual override:
./bin/merge-debian-raspios.sh raspios.img debian.raw -s 16GPartition Expansion
Section titled “Partition Expansion”During merge:
# Resize partition tableparted /dev/loop0 resizepart 2 100%
# Resize ext4 filesysteme2fsck -f /dev/loop0p2resize2fs /dev/loop0p2On first boot (rpi-first-boot.sh):
# Expand to fill entire SD cardparted /dev/mmcblk0 resizepart 2 100%resize2fs /dev/mmcblk0p2rebootTroubleshooting Builds
Section titled “Troubleshooting Builds”QEMU Won’t Boot
Section titled “QEMU Won’t Boot”Symptoms: QEMU hangs at boot
Causes:
- Missing UEFI firmware
- Wrong image format
- Insufficient RAM
Solutions:
# Install UEFI firmwaresudo apt install qemu-efi-aarch64
# Check image formatqemu-img info debian.raw
# Increase RAM in config.shQEMU_RAM="8G"QEMU Timeout
Section titled “QEMU Timeout”Symptoms: Build fails with “QEMU timeout”
Causes:
- Slow network (downloading packages)
- Insufficient resources
- Stuck on interactive prompt
Solutions:
# Increase timeoutQEMU_TIMEOUT=3600 # 1 hour
# Increase resourcesQEMU_RAM="8G"QEMU_CPUS="4"
# Check QEMU logscat qemu-*.logMerge Fails
Section titled “Merge Fails”Symptoms: Error during merge stage
Causes:
- Insufficient disk space
- Corrupted images
- Partition layout mismatch
Solutions:
# Check disk spacedf -h
# Re-download base imagesrm images/debian/raspios-lite.img images/debian/debian-arm64.raw./bin/autobuild --image debian
# Check partition layoutfdisk -l raspios.imgfdisk -l debian.rawImage Won’t Boot
Section titled “Image Won’t Boot”Symptoms: Raspberry Pi won’t boot image
Causes:
- Corrupted SD card
- Wrong fstab UUIDs
- Missing boot files
Solutions:
# Verify image integritysha256sum image.img.xz
# Check SD cardsudo badblocks -v /dev/sdX
# Re-flash imagexz -dc image.img.xz | sudo dd of=/dev/sdX bs=4M status=progress conv=fsyncAdvanced Usage
Section titled “Advanced Usage”Custom RaspiOS Version
Section titled “Custom RaspiOS Version”# In config.shRASPIOS_URL="https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2024-11-24/2024-11-24-raspios-trixie-arm64-lite.img.xz"Custom Debian Version
Section titled “Custom Debian Version”# In config.shIMAGE_URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-arm64.raw"Parallel Builds
Section titled “Parallel Builds”# Build multiple images in parallel./bin/autobuild --image debian &./bin/autobuild --image debian/qemu+docker &waitWarning: Ensure sufficient RAM and disk space for parallel builds.
CI/CD Integration
Section titled “CI/CD Integration”See GitHub Actions for automated builds.