Skip to main content
Version: Torizon OS 6.x.y

Aktualizr - Torizon Update Client Reference Documentation

Introduction

Torizon Remote Updates is the recommended method for Over-the-Air updates using Torizon, our easy-to-use embedded Linux platform. It also supports Secure Offline Updates.

Torizon OS is built with OSTree and Aktualizr. OSTree and Aktualizr are complementary and together they form the foundation for OTA (over-the-air) update capabilities on the device. To learn more about the technical aspects of Torizon Remote and Offline Updates, see the Torizon Updates Technical Overview article.

If you just want to use the Torizon Remote Updates with default settings, the pre-installed software on a Torizon OS image abstracts the settings of Aktualizr for you, therefore you can skip this article.

However, if you need to modify some common configurations of the client-side of Torizon Remote Updates, such as how to block updates, update the server polling time, disable the automatic reboot, among other, or if you just want to learn more about Aktualizr, then this article may be useful for you.

This article will be also useful if you plan to only use the Aktualizr capabilities from Torizon OS, but you don't plan to use the Torizon Remote Updates service.

Prepare for Production

After changing the setting in the SoM, we recommend that you use the TorizonCore Builder Tool to apply customization options to a Torizon OS image for production.

This article complies with the Typographic Conventions for Torizon Documentation.

Prerequisites

Before Starting: Stopping and Restarting the Torizon Remote Updates Client Service

danger

You will need to execute the commands as root. You can log in as root or (better) use the sudo command when logged in with a regular user to do it.

Stopping Aktualizr

Before to start changing the Torizon Remote Updates client settings, make sure that Aktualizr is stopped on the board so it won't interfere while creating configuration files:

# systemctl stop aktualizr

Starting Aktualizr

After applying the changes described below, you need to re-start Aktualizr with the commands below:

# systemctl start aktualizr

You can use journald to view the logs and monitor the operation of the update client:

# journalctl -f -u aktualizr*

Disabling and Enabling Aktualizr

If you don't want Aktualizr to start on every reboot, disable it. If you want to revert it, enable it again:

# systemctl disable aktualizr
# systemctl enable aktualizr

Modifying the Settings of Torizon Remote Updates Client

In this section, we will show how to make common adjustments to Aktualizr settings.

Aktualizr is configured via .toml configuration files. It searches the following directories for files with a .toml extension:

  • /usr/lib/sota/conf.d/
  • /etc/sota/conf.d/

It then processes all the .toml files it finds in alphabetical order. If a config option is specified in multiple files, the last entry overrules any previous entries. Torizon OS includes the following config files by default in /usr/lib/sota/conf.d/:

  • 20-sota-device-cred.toml
  • 30-rollback.toml
  • 40-hardware-id.toml
  • 50-docker-compose.toml
  • 60-polling-interval.toml

If you want to override config options in those files, you should create a config file in /etc/sota/conf.d/ to add or modify config options.

Once you have changed the configuration options to the way that suits you, you can apply the changes to a custom Torizon OS image that you can use for production programming, following the instructions in Capture Changes in the Configuration of a Board on Torizon OS.

Configuring for Offline Updates

Aktualizr's standard configuration is to monitor update requests coming through the Remote Updates method, because of this an Offline Update request will not work out-of-the-box. To reconfigure a device for using Secure Offline updates, you must create or modify a configuration file at /etc/sota/conf.d/. If the folder doesn't exist, you can create it with the following command:

# sudo mkdir -p /etc/sota/conf.d/

Use the following configuration in the file:

# cat /etc/sota/conf.d/99-offline-updates.toml
[uptane]
enable_offline_updates = true
enable_online_updates = false
offline_updates_source = /media/usb/update

This will enable offline updates but disable online updates. For Torizon OS versions before 6.6.0-devel-202402 (monthly), the two update methods were mutually exclusive, i.e. only one method could be enabled at a time, as done above. If you wish to enable both methods, see the next section - Enabling both Online and Offline Updates.

In the sample configuration file, offline_updates_source indicates the folder that aktualizr will monitor for offline updates. In the example, /media/usb it's the path where the relevant USB drive is auto-mounted and update is the name of the actual folder inside that drive containing the update artifacts (i.e. the Lockbox).

info

You also have to restart the aktualizr-torizon client for configuration changes to take place.

Enabling both Online and Offline Updates

Starting with Torizon OS version 6.6.0-devel-202402 (monthly), it is possible to enable online and offline Updates at the same time. This is achieved simply by setting both booleans enable_offline_updates and enable_online_updates to true in the configuration file shown before. In that case, users will be able perform updates via both methods and in whatever order they like. It is also worth mentioning that the offline method will have a higher priority than the other such that, when triggered, it may even interrupt an ongoing online update.

info

Enabling Online & Offline Updates simultaneously requires Torizon OS 6.6.0-devel-202402 or newer.

Configuring the Polling Frequency

In Torizon OS, Aktualizr is configured to poll the server for new updates every 5 minutes. To modify this behavior, we can change the uptane.polling_sec option. For example, to change the polling frequency to 1 hour, you could create a configuration file in /etc/sota/conf.d/ called 61-custom-polling-interval.toml:

# cat <<EOF | sudo tee /etc/sota/conf.d/61-custom-polling-interval.toml 
[uptane]
polling_sec = 3600
EOF

After applying the changes, don't forget to Restart Aktualizr.

Configuring the Hardware ID

When Toradex publishes our Torizon OS images to the OTA server, their metadata includes a list of which hardware IDs they are compatible with. This ensures that you don't accidentally send an image built for a colibri-imx7 to an apalis-imx8. This hardware compatibility is enforced by the update client: if your device reports that it is an apalis-imx8, it will refuse any update images that don't list apalis-imx8 as a compatible hardware ID.

Normally, it's best not to modify the hardware ID your device reports. If you give it a custom hardware ID, it will no longer accept stock Torizon OS images. But sometimes, that's exactly what you want: for example, if you have several different SKUs that need slightly different OS images, or if you have products with the same SoM but a different carrier board or peripherals. In those cases, you might want to make sure that each SKU or variant has its own hardware ID, and then publish a customized Torizon OS build for each one; that way you can make sure that nobody can accidentally create an OTA update that sends incompatible OS images.

To achieve that, you need to do two things:

  • Modify the Aktualizr config on your device image to report a custom hardware ID
  • Upload the customized image to Torizon Remote Updates along with metadata indicating it's compatible with your custom hardware ID

Configuring the Hardware ID on Torizon OS

To configure the hardware ID in the Aktualizr config, you need to set the provision.primary_ecu_hardware_id config value. For example, if you wanted your device to be registered with the hardware ID apalis-imx8-sku001, you could create a configuration file in /etc/sota/conf.d/ called 41-custom-hardware-id.toml:

# cat <<EOF | sudo tee /etc/sota/conf.d/41-custom-hardware-id.toml 
[provision]
primary_ecu_hardware_id = apalis-imx8-sku001
EOF
info

Because this is a change to provisioning configuration, you will need to re-provision the device if you want to change it on a live device. It's highly recommended to apply the changes to a custom Torizon OS image that you can use for production programming, following the instructions in Capture Changes in the Configuration of a Board on Torizon OS. That way, your devices will use their custom hardware ID as soon as they are programmed.

Configuring Custom Hardware IDs for Torizon Remote Updates Images

When sending a custom image to the OTA server, you can specify a list of hardware IDs it is compatible with. Using the push command of the TorizonCore Builder Tool, you can specify one or more --hardwareid arguments to override the image's compatible hardware IDs.

$ torizoncore-builder push my-branch --credentials credentials.zip --package-name my-custom-image --hardwareid apalis-imx8-sku0001 

Each image can have several hardware IDs assigned to them. To add multiple hardware IDs to a custom image, use the --hardwareid argument once for each hardware ID.

$ torizoncore-builder push my-branch --credentials credentials.zip --package-name my-custom-image --hardwareid apalis-imx8-sku0001 --hardwareid apalis-imx8-sku0003 --hardwareid apalis-imx8-sku007-beta

Remember, Torizon Remote Updates will only permit you to install an image on a device if the hardware IDs match.

Configuring the loglevel

Aktualizr produces logs at different levels of detail depending on the loglevel setting:

  • trace (0) logs everything, including the content of every HTTP request
  • debug (1) has extra information useful for debug purposes, but is not as verbose as trace
  • info (2) is the default, and logs basic information about normal and exceptional Aktualizr operations
  • warning (3) only logs warnings and other exceptional information
  • error (4) only logs errors
  • fatal (5) only logs fatal errors

If you are troubleshooting an issue with the OTA client, it may be helpful to increase the loglevel. To do this, we can change the logger.loglevel option. For example, to enable trace logging, you could create a configuration file in /etc/sota/conf.d/ called 99-custom-loglevel.toml:

# cat <<EOF | sudo tee /etc/sota/conf.d/99-custom-loglevel.toml 
[logger]
loglevel = 0
EOF
info

More detailed loglevels, especially trace, can write a large volume of log data if left on for a long time. We recommend leaving the loglevel at the default except when specifically needed for troubleshooting.

After applying the changes, don't forget to Restart Aktualizr.

Allowing and Blocking Updates

You can lock Aktualizr to avoid receiving new updates on the client.

Every time before applying an update, Aktualizr attempts to acquire a lock on /run/lock/aktualizr-lock using flock.

To help control when updates are applied, you can have a custom code in your application(s) that acquires and releases this lock (see flock (2) man page for more details).

If you are using the command line, it is possible to apply an advisory lock on a open file using the command flock. For example, the command below will apply an advisory lock on /run/lock/aktualizr-lock for 30 seconds:

# sudo flock --verbose -x /run/lock/aktualizr-lock -c "sleep 30"

Enabling and Disabling Automatic Reboot

By default, Torizon OS is configured to automatically reboot the device after a successful update of the operating system. If you want to disable this feature, run the commands below:

# systemctl stop ostree-pending-reboot.path
# systemctl disable ostree-pending-reboot.path

In case you disabled the automatic reboot for system updates and want to re-enable it, run the commands below:

# systemctl enable ostree-pending-reboot.path
# systemctl start ostree-pending-reboot.path

After applying the changes, don't forget to Restart Aktualizr.

Changing the Default Reboot Command

By default, Aktualizr will use /sbin/reboot to reboot the device after a successful update (if enabled). To modify the command used to reboot the device, we can change the bootloader.reboot_command option. For example, you could create a configuration file in /etc/sota/conf.d/ called 21-custom-reboot.toml pointing to a custom reboot script:

# cat <<EOF | sudo tee /etc/sota/conf.d/21-custom-reboot.toml 
[bootloader]
reboot_command = "/my-custom-reboot-command"
EOF

Advanced Aktualizr Config Options

Aktualizr has many more config options available. For more details, please consult the project's official documentation.

Setting the Device ID and Name During Provisioning

When you provision a device with Torizon Remote Updates, it will be assigned a Device ID and a Device Name. Both Device ID and Device Name must be unique in your account. By default, Device ID is derived from the SoM model and serial number, and Device Name is generated from a random wordlist.

If you want to provide those values yourself, you can add them to the provisioning command by adding the -d option for Device ID and the -n option for Device Name. Your provisioning command will normally be of the form docker run -v /:/som_sysroot --network=host --privileged -it torizon/torizon-provisioner:0.0.11 provision-device -t ${token} && sudo systemctl restart aktualizr fluent-bit. To set the custom Device ID and Device Name, you would change that to docker run -v /:/som_sysroot --network=host --privileged -it torizon/torizon-provisioner:0.0.11 provision-device -t ${token} -d "my-device-id" -n "My Product 0053" && sudo systemctl restart aktualizr fluent-bit. Note that the added parameters need to come before the &&.

Configuring Secondaries

info

regular users of Torizon Remote Updates don't need to configure secondaries, they are already configured by default.

Secondary is a concept in Uptane-compatible OTA systems that make it possible to update not only the main operating system but also other firmware and devices connected to it. Torizon OS uses secondaries to update containers via docker-compose files. Secondaries are also used in Subsystem Updates and Bootloader Updates

If you want to override the default configuration - located at paths under /usr, which is ready-only - take them as starting point and create custom configuration files under /etc/sota/conf.d/. Aktualizr first reads the configuration files from /usr/lib/sota/conf.d/ and loads it. If any custom configuration files are found in /etc/sota/conf.d/, the respective configurations are overridden. The secondary configuration .toml file points to the secondary definition .json file.

To get the content from the default files, execute the commands below.

Secondary configuration .toml file (copy it from /usr/lib/sota/conf.d and edit under /etc/sota/conf.d/ to define the path for your secondary definition .json file):

# cp /usr/lib/sota/conf.d/50-secondaries.toml /etc/sota/conf.d/50-secondaries.toml 
# vi /etc/sota/conf.d/50-secondaries.toml
[uptane]
secondary_config_file = "/etc/sota/secondaries.json"

Secondary definition .json file (copy it from /usr/lib/sota and edit under /etc/sota/):

# cp /usr/lib/sota/secondaries.json /etc/sota/secondaries.json
# cat /etc/sota/secondaries.json
{
"docker-compose": [
{
"partial_verifying": false,
"ecu_hardware_id": "docker-compose",
"full_client_dir": "/var/sota/storage/docker-compose",
"ecu_private_key": "sec.private",
"ecu_public_key": "sec.public",
"firmware_path": "/var/sota/storage/docker-compose/docker-compose.yml",
"target_name_path": "/var/sota/storage/docker-compose/target_name",
"metadata_path": "/var/sota/storage/docker-compose/metadata"
}
],
"torizon-generic": [
{
"partial_verifying": false,
"ecu_hardware_id": "verdin-imx8mp-bootloader",
"full_client_dir": "/var/sota/storage/bootloader",
"ecu_private_key": "sec.private",
"ecu_public_key": "sec.public",
"firmware_path": "/var/sota/storage/bootloader/u-boot.img",
"target_name_path": "/var/sota/storage/bootloader/target_name",
"metadata_path": "/var/sota/storage/bootloader/metadata",
"action_handler_path": "/usr/bin/bl_actions.sh"
}
]
}

Edit to add your custom configuration. For example, add a new secondary for a subsystem update - maintaining the other definitions:

/etc/sota/secondaries.json
{
"docker-compose": [
{
"partial_verifying": false,
"ecu_hardware_id": "docker-compose",
"full_client_dir": "/var/sota/storage/docker-compose",
"ecu_private_key": "sec.private",
"ecu_public_key": "sec.public",
"firmware_path": "/var/sota/storage/docker-compose/docker-compose.yml",
"target_name_path": "/var/sota/storage/docker-compose/target_name",
"metadata_path": "/var/sota/storage/docker-compose/metadata"
}
],
"torizon-generic": [
{
"partial_verifying": false,
"ecu_hardware_id": "verdin-imx8mp-bootloader",
"full_client_dir": "/var/sota/storage/bootloader",
"ecu_private_key": "sec.private",
"ecu_public_key": "sec.public",
"firmware_path": "/var/sota/storage/bootloader/u-boot.img",
"target_name_path": "/var/sota/storage/bootloader/target_name",
"metadata_path": "/var/sota/storage/bootloader/metadata",
"action_handler_path": "/usr/bin/bl_actions.sh"
},
{
"partial_verifying": false,
"ecu_hardware_id": "my-secondary",
"full_client_dir": "/var/sota/storage/my-secondary",
"ecu_private_key": "sec.private",
"ecu_public_key": "sec.public",
"firmware_path": "/var/sota/storage/my-secondary/my-payload.file",
"target_name_path": "/var/sota/storage/my-secondary/target_name",
"metadata_path": "/var/sota/storage/my-secondary/metadata",
"action_handler_path": "/usr/bin/my_secondary_actions.sh"
}
]
}

After making changes, don't forget to Restart Aktualizr.

Configuring Options for Low-Bandwidth Connections

info

These options are only available in Torizon OS 6.5.0 or later.

Torizon Remote Updates can present some limitations when dealing with very constrained low-bandwidth connections. These limitations usually manifest as either timeouts or various connection/download errors.

There are a couple of options that can be tweaked to improve update stability in these situations. These options affect the download behavior of OS updates, specifically with how OSTree performs downloads.

To set these options, you must pass environment variables to Aktualizr. Therefore, you it is needed to modify the systemd service that launches Aktualizr.

When editing the systemd service, the environment variables that can be set to control these options are:

[Service]
...
Environment="OSTREE_CURL_TIMEOUT=<timeout_setting_in_s>"
Environment="OSTREE_CURLM_MAX_TOTAL_CONN=<max_number_of_connections>"
...
  • OSTREE_CURL_TIMEOUT: This sets the timeout, in seconds, for curl requests performed by OSTree. This timeout is per individual curl request and not for the entire update itself. By default this is about 13 minutes. Try increasing this if you find your connection is slow enough to trigger the default timeout.
  • OSTREE_CURLM_MAX_TOTAL_CONN: This sets the max amount of parallel connections that can be used for downloads with OSTree. By default this will automatically optimize to use the max amount of parallel downloads possible. Decreasing the number here will increase the overall time it takes for your OS update to complete. However, less parallel downloads can improve download stability. Since more downloads occuring in parallel increase the chance of a connection error when working with a poor, or low-bandwidth connection.

After applying changes to the systemd service, don't forget to Restart Aktualizr.

Subsystem Updates Resources

The following section brings information about environment variables that Aktualizr provides for the Action handler script or program execution.

Environment Variables

Environment variables passed to the action-handler upon every action:

VariableDescription
SECONDARY_INTERFACE_MAJORMajor version of the interface; this will be increased upon breaking changes. Initial major is 1.0.
SECONDARY_INTERFACE_MINORMinor version of the interface; this will be increased upon non-breaking changes. Initial minor is 0.
SECONDARY_HARDWARE_ID
I/F version: 1.0
Hardware ID is the identification of the target subsystem for the update.
SECONDARY_ECU_SERIAL
I/F version: 1.0
Serial number of the target subsystem being updated; this is a unique number identifying a system component that could be a target for an update.

Environment variables for the get-firmware-info action:

VariableDescription
SECONDARY_FWINFO_DATA
I/F version: 1.0
JSON string with all available data for the action (Reserved for future use).
SECONDARY_FIRMWARE_PATH
I/F version: 1.0
Full-path to the payload file. Upon the “get-firmware-info” action, the file may not exist if there was no previous successful installation. Your get-firmware-info script part can use this information to compare the last provided payload with the currently installed version.

Environment variables for the install action:

VariableDescription
SECONDARY_INSTALL_DATA
I/F version: 1.0
JSON string with available information for the action (Reserved for future use).
SECONDARY_UPDATE_TYPE
I/F version: 1.0
Type of update being performed: online or offline.
SECONDARY_FIRMWARE_PATH
I/F version: 1.0
Full-path to the payload file to be installed. Upon the “install” action, this path would point to a new payload file that will replace the previous one (if installation is successful).
SECONDARY_FIRMWARE_SHA256
I/F version: 1.0
The SHA-256 checksum of the firmware file to be installed.
SECONDARY_CUSTOM_METADATA
I/F version: 1.0
JSON string containing the merged custom metadata from the director and image Uptane repositories associated with the target (package) being installed.

Environment variables for the complete-install action:

VariableDescription
SECONDARY_CMPLINSTALL_DATA
I/F version: 1.0
JSON string with all available data for the action (Reserved for future use).
SECONDARY_FIRMWARE_PATH
I/F version: 1.0
Full-path to the firmware file to be installed. Upon the “complete-install” action, this path would point to a new firmware file that will replace the previous one (if installation is successful).
SECONDARY_FIRMWARE_SHA256
I/F version: 1.0
The SHA-256 checksum of the firmware file to be installed.
SECONDARY_CUSTOM_METADATA
I/F version: 1.0
JSON string containing the merged custom metadata from the director and image Uptane repositories associated with the target (package) being installed.

Exit Codes

The following exit codes will be received from the action-handler execution:

Exit CodeDescription
0 (Action handled)Indicate that the action was handled by the handler which in turn should also output a JSON string (into its standard output) with information specific to the executed action.
64 (Request normal processing)Indicate that the action was not handled by the handler but that the Aktualizr update client can move on by performing its normal (non-error) processing for that action if possible.
65 (Request error processing)Indicate that the action was not handled by the handler and that the Aktualizr update client should consider the situation as a failure.
66 (Abort processing)Reserved for future use

Update Checks and Rollbacks

Greenboot (Generic Health Check Framework) is a Fedora project that helps manage systemd services health. Torizon OS uses Greenboot as a framework to make update checks and rollbacks more flexible and manageable by the user.

By default, Torizon OS will consider a successful boot if the boot-complete systemd target is successfully executed. This is because the main operating system services required for proper operation, including the Docker daemon, are inside boot-complete.target. And if boot-complete.target fails during an update, Torizon OS will automatically reboot, and after three tries, it will rollback to the previous operating system version.

In case you want to add additional checks to confirm a successful update, you can add shell scripts to /etc/greenboot/check/required.d/. As a convention, the script name should start with two numbers and finish with .sh (Example: 01_check_system.sh). Scripts in /etc/greenboot/check/required.d/ will be executed as part of the boot health checks.

If the scripts in /etc/greenboot/check/required.d/ are successfully executed, and the boot-complete.target is successfully started, the system will enter in the GREEN state. In this case, the greenboot-task-runner service will be triggered, and user-defined scripts inside /etc/greenboot/green.d/ will be executed. These scripts can be used to execute post-install operations during a successful update, but be aware that they will run every time the operating system boots.

Now, if one of the scripts in /etc/greenboot/check/required.d/ fail (exit code is not 0), the boot-complete.target will also fail, and the system will enter in the RED state. In this case, the redboot-task-runner service will be triggered, and user-defined scripts inside /etc/greenboot/red.d/ will be executed. These scripts can be used to execute post-install operations during a failed update, but be aware that they will run every time the operating system boots (when boot-complete.target fails). After redboot-task-runner service finishes execution, the redboot-auto-reboot service is triggered, and this service will run a script that will reboot the system (in case an update is in progress). After 3 reboots, the system will rollback to the previous operating system version.

To confirm if a boot was successful (GREEN status) or if it failed (RED status), you can check the status of the greenboot-status systemd service:

$ systemctl status greenboot-status
* greenboot-status.service - greenboot MotD Generator
Loaded: loaded (/usr/lib/systemd/system/greenboot-status.service; enabled; vendor preset: enabled)
Active: active (exited) since Mon 2021-04-26 12:50:04 UTC; 16min ago
Process: 809 ExecStart=/usr/libexec/greenboot/greenboot-status (code=exited, status=0/SUCCESS)
Main PID: 809 (code=exited, status=0/SUCCESS)

Apr 26 12:50:04 apalis-imx6-05039068 systemd[1]: Starting greenboot MotD Generator...
Apr 26 12:50:04 apalis-imx6-05039068 greenboot-status[814]: Boot Status is GREEN - Health Check SUCCESS
Apr 26 12:50:04 apalis-imx6-05039068 systemd[1]: Started greenboot MotD Generator.

Alternatively, you can list the content of the /run/boot-status file:

$ cat /run/boot-status
Boot Status is GREEN - Health Check SUCCESS

For more information about the Greenboot framework, please see the project's website.

Troubleshooting

To see in more detail what Aktualizr is doing, you can stop the systemd service and start Aktualizr manually, e.g. with an increased loglevel for debugging:

# systemctl stop aktualizr
# aktualizr-torizon --loglevel 1
# systemctl stop aktualizr
# aktualizr --loglevel 1

To provision the device again, make sure to stop Aktualizr, remove the device and start Aktualizr again:

# rm /var/sota/sql.db
# rm -rf /var/sota/storage/

Common Issues

If you see the following message:

response http code: 400
response: "An error occurred: Missing entity: Ecu"
could not put manifest

Make sure to properly delete the storage of the secondary (see above).



Send Feedback!