Skip to content
Notifications
Clear all

TorizonOS: ready for production

1 Posts
1 Users
0 Reactions
10 Views
David Truan
(@david)
Member
Joined: 6 months ago
Posts: 12
Topic starter   [#58]

TorizonOS: Integrating TEZI into a Production Workflow

In a previous post, we took TEZI apart by hand: we extracted the Yocto tarball, edited image.json, injected our own ITB and boot script with a filelist, swapped imx-boot, and set autoinstall to true. It worked, and it was a good way to understand TEZI. But as that post noted, hand-editing those files is not officially supported and does not scale.

So today we'll cover the production side: how the same TEZI image is produced in a reproducible, supported way with TorizonCore Builder, and how TEZI fits into a provisioning line where boards come off the bench already flashed, configured, and registered to the cloud.

Why hand-editing does not scale

Editing image.json on a USB stick is fine for one board on your desk. Across a production run it falls apart: nothing stops an operator forgetting a filelist entry or leaving autoinstall off, a hand-edited stick has no version history, and none of those manual edits are supported if something breaks. The fix is to stop patching the TEZI image and start building it from versioned inputs. That is what TorizonCore Builder does.

TorizonCore Builder

TorizonCore Builder (TCB) is the officially supported command-line tool for customizing a Torizon OS image. It runs on your host (x64 Linux, or Windows via WSL2) for both development and production.

The command that matters is build. It takes a single tcbuild.yaml file describing a base image, customizations, and an output, then produces a customized image in one stateless step: same inputs, same output, every time. The build becomes a script you commit to Git and run in CI instead of a manual procedure.

The result is still a Toradex Easy Installer image: the same image.json, OSTree archive, and tarball layout we dissected last time. TEZI does not change. Everything we edited by hand is just generated from a declarative file now.

The tcbuild.yaml file

The file has three top-level sections that map onto what we were doing manually:

input:
  easy-installer:
    local: torizon-docker-verdin-imx8mp-Tezi.tar
customization:
  # device trees, overlays, kernel modules, splash, captured config...
output:
  easy-installer:
    local: torizon-custom-verdin-imx8mp
    autoinstall: true
    autoreboot: true
    accept-licence: true
  • input: the base image to start from: the off-the-shelf TEZI tarball, or a Torizon OS image from your own Yocto build (more below).
  • customization: what you layer on top: device trees, overlays, external kernel modules, splash screens, and captured config.
  • output: the resulting TEZI image and its switches. autoinstall: true is the supported version of the image.json edit we did by hand, and autoreboot: true boots straight into the flashed system. accept-licence: true matters here because the Verdin iMX8M Plus is an NXP module: its image carries an NXP EULA (the LA_OPT_NXP_SW.html from last time), and unattended autoinstall will not run unless that licence is accepted.

Because the input, the artifacts, and the YAML are all just files, you can commit them to Git and rebuild the exact same image whenever you need.

Where our custom boot fits

One honest caveat for the setup in the first post. TCB customizes the OS side: the OSTree rootfs, device trees, overlays, kernel modules, splash, and config. It does not rebuild your bootloader. Toradex lists this explicitly: TCB cannot modify the kernel source, add in-tree kernel modules, customize U-Boot, or change the initramfs, and points you at Yocto for all four. Our custom ITB with its own U-Boot, SPL, and ATF lands squarely in that territory.

So in production those boot artifacts belong in the Yocto build, not on a patched USB stick. You integrate the custom U-Boot, ITB, and boot script into your BSP layer and build from source, so the TEZI tarball already carries the right imx-boot. TCB then takes that image as its input and layers containers, config, and provisioning on top. The split is clean:

  1. Yocto / BSP: the boot chain (U-Boot, SPL, ATF, ITB, boot script).
  2. TorizonCore Builder: OS customization, container bundling, cloud provisioning.
  3. TEZI: flashes the result, exactly as before.

Bundling containers and capturing config

Two TCB features carry most of the production load.

Container bundling lets TCB pull your Docker images at build time and bake them into the OS image, so they run on first boot with no docker pull on the line. It goes under output.easy-installer:

output:
  easy-installer:
    local: torizon-custom-verdin-imx8mp
    bundle:
      compose-file: docker-compose.yml

This applies to the Docker variant of Torizon OS, which torizon-docker-verdin-imx8mp already is.

Capturing config replaces "remember to set the network and update settings": you configure one golden device, capture the diff into a changes directory with TCB, and feed it to build. The same tweaks then land on every device.

Flashing in production with TEZI

Once build produces the image, TEZI installs it, two common ways:

  1. Physical media (USB or SD): copy the image on, plug it in, and with autoinstall set TEZI flashes with no operator and (with autoreboot) boots straight up. The supported version of what we forced by hand last time.
  2. LAN over Ethernet: for boards with no media access, torizoncore-builder images serve exposes the image over the LAN with a pre-configured Avahi Zeroconf service, which TEZI discovers automatically. Power on the board and it installs unattended.

Either way you get the single-step, display-free provisioning we were chasing last time, now from a reproducible pipeline.

Provisioning at scale to Torizon Cloud

One thing hand-editing never touched: registering each board to TorizonCloud for secure over-the-air updates.

First, fetch your account's provisioning data once:

torizoncore-builder platform provisioning-data \
  --credentials credentials.zip \
  --shared-data shared-data.tar.gz \
  --online-data DEFAULT

That gives you two pieces: shared-data (a tarball, not sensitive, used for offline and online updates) and online-data (a base64 string, sensitive, online updates only, keep it out of Git). Add them to the same output section:

output:
  easy-installer:
    local: torizon-custom-verdin-imx8mp
    provisioning:
      mode: "online"
      shared-data: shared-data.tar.gz
      online-data: ${ONLINE_DATA}
      fleets:
        - "<FLEET_UUID>"

The ${ONLINE_DATA} variable keeps the sensitive string out of the file, and the optional fleets list auto-assigns each device to fleets in your account.

After that it is hands-off. Each board runs an auto-provisioning service on boot, registers itself, retries every few minutes if there is no network yet, and is then ready for updates and monitoring. Check it with systemctl status auto-provisioning; a "Device successfully provisioned" line means it joined your fleet.

Wrapping up

TEZI does not change in production. It still partitions the eMMC, writes the bootloader, and unpacks the rootfs. What moves upstream is everything we used to do with a text editor on a USB stick: the boot chain goes into the Yocto build, and OS customization, containers, config, autoinstall, and provisioning go into one tcbuild.yaml that TCB turns into a TEZI image, reproducibly, every run.

The end result is a board that comes off the line flashed, configured, running its containers, and registered for updates, from one versioned recipe and a single power cycle.

References



   
Quote
Share: