Welcome to the RAUC Image Creation exercise!
Mender (an alternative OTA solution) has a really good lecture about how A/B updates work. Though we will be using RAUC as lightweight update framework.
You will create a bootable disk image for an embedded system with multiple partitions (boot, rootfsA, rootfsB, data), install a kernel, configure U-Boot with environment support, and install RAUC in both rootfs partitions.
Oh, and you will also finally get to use a more familiar Linux distro as root filesystem: yep, Debian!
Everything will be scripted for reusability (don't worry, they are almost written, though you will need to fill in some TODOs).
Download here the starter archive containing skeleton files + scripts.
Extract it somewhere in a new directory (e.g., create rauc-lab
).
The code structure will become (mostly is, but some are TODO) as follows:
rauc-lab/ ├── artifacts/ # <-- you'll create this │ ├── flash_spl.bin # A working flash.bin image you already have │ ├── flash_emmc.bin # Your new flash.bin (see tasks) │ ├── linux.itb # A kernel image (after removing the initrd) │ ├── uboot.env # U-Boot environment variables (just the var file) ├── utils/ │ ├── ca.cert.pem │ ├── rauc-mark-good.service │ ├── system.conf │ └── fstab # Optional custom fstab ├── scripts/ # <-- your main job to finish those! │ ├── 05-debootstrap.sh │ ├── 10-create-base-disk.sh │ ├── 11-populate-disk-image.sh │ ├── 20-modify-bootcmd.sh │ ├── 21-install-boot.sh │ └── 30-install-rauc.sh ├── run-all.sh # Will run all scripts in order
First, make sure you have these packages installed on your host system (they were already installed on the VM, so you can skip this):
sudo apt install qemu-user-static binfmt-support debootstrap parted dosfstools e2fsprogs u-boot-tools
Then, check that binfmt_misc
is active and register the QEMU handler for aarch64
:
grep aarch64 /proc/sys/fs/binfmt_misc/qemu-aarch64
If it’s empty, run:
sudo su echo ':qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff:/usr/bin/qemu-aarch64-static:CF' > /proc/sys/fs/binfmt_misc/register exit
For Arch Linux users: check out this guide for installable dependencies!
You’ll be running scripts in ./scripts
, either one by one or all together via ./run-all.sh
.
The scripts are templates — you may need to complete missing parts (search for TODO
s)!
Yes, really, you can easily do this!
You will use the debootstrap official tool to install a minimal Debian (you can also install deb-based derivatives like Ubuntu!) base system.
📄 Edit the 05-debootstrap.sh
script.
Since we need to install the Debian binaries executable on a 64-bit ARM architecture, we will need to split the installation into two stages: first, the .deb (Debian install packages) are downloaded from a Debian mirror server and unpacked into the target rootfs directory. Afterwards, since Debian will need to run some scripts to setup its distro system, we must emulate the target architecture. Enter qemu which will help us to just that!
Finally, we need to obtain the artifacts/debrootfs.tar.gz
archive with the contents of our newly-created Aarch64 Debian. Use tar
to c
reate a gz
ipped archive, and be sure to p
reserve permissions!
The final results should be something like:
-rw-r--r-- 1 root root 148M 2025-08-07 14:23 debrootfs.tar.gz
📄 File: 10-create-base-disk.sh
Your task:
truncate
or dd
to allocate the image with the desired size;parted
invocation (script);
📄 File: 25-populate-base-disk.sh
Subtasks:
debrootfs.tar.gz
on each rootfs A/B partition (a simple tar xf
will suffice);
Note: you will lack flash_emmc.bin
required by this script. So let's proceed without testing, for now.
If you wish to enter your newly bootstrapped Debian rootfs, you can use the following script:
#!/bin/bash set -e ROOTFS=$1 if [[ -z "$ROOTFS" ]] || [[ ! -d "$ROOTFS/usr/bin" ]]; then echo "[!] Invalid rootfs argument: '$ROOTFS'" exit 1 fi cleanup() { echo "[*] Cleaning up..." for sub in dev/pts dev proc sys run; do if mountpoint -q "$ROOTFS/$sub"; then echo " Unmounting $ROOTFS/$sub" sudo umount "$ROOTFS/$sub" || true fi done } trap cleanup EXIT echo "[+] Binding chroot filesystems..." for fs in dev dev/pts proc sys run; do sudo mount --bind "/$fs" "$ROOTFS/$fs" done echo "[+] Ensuring /etc/resolv.conf for DNS inside chroot" sudo cp /etc/resolv.conf "$ROOTFS/etc/resolv.conf" echo "[+] Entering your chroot shell..." echo "Type exit to leave" sudo chroot "$ROOTFS" /usr/bin/qemu-aarch64-static /bin/bash
You can even run apt install
in there ;)
For this task, you will need to configure u-boot with a default environment as documented in this previous lab.
Simply paste the recommended default and use menuconfig
to enable it (don't worry about the bootcmd
, we will replace it here with a custom script).
Now, edit the 20-modify-bootcmd.sh
script and set the path to your default.env
file. Read its source code to see what it does and run it!
Check out your default file again. It shoule have a quite large boot command concatenated from the source utils/boot.cmd.in
script.
Now pay attention: make a backup of your current flash.bin
file you used to boot using uuu -b spl
. You'll need it later to flash the emmc, as the one you'll build in the following instructions will not work (you will override the boot command!). Put the backup file into artifacts as flash_spl.bin
!
Let's get this over with: re-build your u-boot, copy all resulting binaries (uboot-imx/*.bin
, uboot-imx/spl/*.bin
) back into imx-mkimage/iMX93/
, invoke the imx-mkimage
make command again to obtain a new flash.bin
file. Store it as artifacts/flash_emmc.bin
(will get to reside on the eMMC persistent storage).
We still have some work to do before generating the disk image…
We must pack our kernel + device tree again, but this time without the Buildroot initramfs (we don't need it since we've got 2xDebians now!). So back up your linux.its
(surely you don't want to waste your work so far, right?), then modify the original and simply delete the initrd
block from images
and the ramdisk = “initrd”;
property.
Rebuild the Linux FIT uImage to obtain your new linux.itb
, which you'll then copy to rauc-lab/artifacts/linux.itb
.
Now it's time to run 21-install-boot.sh
. What's that? it wants a .env
file? You can create an empty one ;)
Finally, it comes down to installing RAUC into the Debian partitions.
📄 Our script: 15-install-rauc.sh
is already complete. Feel free to read and understand it.
This script will:
rauc
ca.cert.pem
(certificate used to sign the update images)system.conf
rauc-mark-good.service
/etc/fw_env.config
, fstab
, and enables systemd servicesFinally, run it!
Just make sure:
rauc.service
is enabled in both roots;
We can now run 25-populate-disk-image.sh
. The artifacts/disk.img
will be updated, Debian will be copied to the first 2 EXT4 partitions and the bootloader will be installed at 32KB offset (the SoC's BL1/BOOTROM wants it there).
Power up the board. Use uuu -b emmc_all flash_spl.bin disk.img
to flash it (hope you backed it up, since the modified flash_emmc.bin
version won't work)!
Our Debian's default login: root
+ root
;)
Once Debian booted, check:
mount | grep rootfs cat /etc/rauc/system.conf