In today's post, we'll cover how Toradex Easy Installer TEZI installs a TorizonOS image. We'll also learn how to modify it to add customs components and to be able to automatically install an image. This post also extends the one written by my colleague here.
It is to note that the image customization part of this post is not officially supported by Toradex and is more of a "dive deeper" in TEZI than an official guide. A future post will cover a more production ready procedure, while this one is more of a discovery/custom guide.
TEZI introduction
TEZI is the official way to flash an Torizon OS image in Toradex ecosystem. It is a simple Linux running in RAM, flashed over USB after booting the board into recovery. The Toradex boards all come pre-flashed with TEZI in their eMMC, removing the need of setting up the recovery process.
TEZI's job is simple:
- Set up the storage and network
- Scan Toradex official feeds (network) and any storage peripherals (USB, SD Card)
- Launch a UI over the HDMI listing the images and some settings
- Flash the image once it is selected by the user
- Ask to reboot
And voilà, TEZI's job is done! So what does TEZI look at to flash the image?
TEZI bundled files
After a successful Yocto build, the TorizonOS image is packaged as a tarball ready for TEZI:
torizon/build/deploy/images/verdin-imx8mp/torizon-docker-verdin-imx8mp-Tezi.tar
Extracting it gives:
. ├── image.json ├── imx-boot ├── torizon-docker-verdin-imx8mp.ota.tar.zst ├── u-boot-initial-env-sd ├── prepare.sh ├── wrapup.sh ├── marketing.tar ├── toradexlinux.png └── LA_OPT_NXP_SW.html
The key files are:
image.json:the descriptor that tells TEZI how to handle everythingimx-boot:U-Boot + SPL, combined with LPDDR firmware and ARM Trusted Firmwaretorizon-docker-verdin-imx8mp.ota.tar.zst:the OSTree rootfs archiveu-boot-initial-env-sd:the initial U-Boot environment
image.json
This file is what TEZI reads first. It describes the image metadata and, how each block device should be partitioned and populated. A standard, unmodified file looks like this:
{
"config_format": "4",
"autoinstall": false,
"name": "Torizon OS",
"description": "Torizon OS Linux with no containers pre-provisioned.",
"version": "7.4.0-devel-20250930075455+build.0",
"release_date": "2025-09-30",
"u_boot_env": "u-boot-initial-env-sd",
"prepare_script": "prepare.sh",
"wrapup_script": "wrapup.sh",
"supported_product_ids": ["0058", "0061", "0063", "0064", "0065", "0066", "0070"],
"blockdevs": [
{
"name": "emmc",
"partitions": [{
"partition_size_nominal": "512",
"want_maximised": true,
"content": {
"label": "otaroot",
"filesystem_type": "ext4",
"mkfs_options": "-E nodiscard",
"filename": "torizon-docker-verdin-imx8mp.ota.tar.zst",
"uncompressed_size": 674.6015625
}
}]
},
{
"name": "emmc-boot0",
"erase": true,
"content": {
"filesystem_type": "raw",
"rawfiles": [{"filename": "imx-boot", "dd_options": "seek=0"}]
}
}
]
}
Breaking down the important fields:
autoinstall:whentrue, TEZI will install the image automatically upon detection, without any user interaction. Useful for headless provisioning.supported_product_ids:the list of Toradex product IDs this image supports. TEZI uses this to validate hardware compatibility before flashing.blockdevs:the core of the file. Describes what goes where on each block device.emmc: the main eMMC partition, formatted as ext4, receives the OSTree rootfs.emmc-boot0: the hardware boot partition (4 MiB), written in raw mode at offset 0 withimx-boot.
prepare_script/wrapup_script:shell scripts that TEZI runs before and after flashing, respectively. Used for board-specific setup.
Customizing the flash
The standard TorizonOS image boots using Toradex's own U-Boot and device tree. In our project, we need to boot from our own ITB (Image Tree Blob), a U-Boot FIT image that bundles our custom kernel, device tree, and initramfs into a single signed artifact, using a custom boot script to load it.
TEZI supports copying additional files into the ext4 partition through the filelist key inside a partition's content block. We use this to inject our ITB and boot script at flash time, alongside the OSTree rootfs:
"content": {
"label": "otaroot",
"filesystem_type": "ext4",
"mkfs_options": "-E nodiscard",
"filename": "torizon-docker-verdin-imx8mp.ota.tar.zst",
"uncompressed_size": 674.7265625,
"filelist": [
"verdin-imx8mp.itb",
"boot.edgem.scr"
]
}
TEZI extracts the rootfs archive first, then copies each file listed in filelist directly into the partition root. Both verdin-imx8mp.itb and boot.edgem.scr must be present alongside image.json on the USB drive.
The second change is setting "autoinstall": true. This instructs TEZI to skip the selection UI and flash immediately upon detecting the USB drive, making it possible to provision boards with no display and no operator interaction.
Once again, this is not the official way of modifying the TorizonOS image. To customize an image it without manually modifying those files, one must use TorizonCore Builder.
Flashing the board
Preparing a USB drive for deployment comes down to:
- Extracting the Yocto-produced TEZI tarball onto an ext4-formatted USB stick.
- Replacing
imx-bootwith our custom build (U-Boot + SPL + ATF). - Adding
verdin-imx8mp.itbandboot.edgem.scrnext toimage.json. - Editing
image.jsonto add thefilelistentries and setautoinstalltotrue.
Once plugged in, TEZI handles the rest: it partitions the eMMC, writes the bootloader to emmc-boot0, extracts the OSTree rootfs, and copies the ITB and boot script into the ext4 partition. On the next power cycle, our custom U-Boot finds verdin-imx8mp.itb at the root of the partition and hands off control to it.
The result is a board fully provisioned through TEZI in a single step, without even needing a display!
