Skip to main content

First OTA

In this mission you go from zero to a verified over-the-air (OTA) update. You create a project from the heartbeat reference, connect it to Avocado Connect, and boot it on a QEMU virtual device. Then you add a new metric to the heartbeat script, publish it as a new runtime, deploy it, and watch the new field appear in the device's logs. The update merges live, with no reboot.

The pipeline is the same one you would use for a production fleet. Only the device is virtual.

Along the way you'll see each step done multiple ways where multiple tools support it: uploading a runtime from the Avocado CLI or Avocado Desktop, and creating a deployment from the CLI, Desktop, or the Avocado Connect console.

How an OTA works in Avocado​

  • A runtime is the versioned artifact you deploy. You build it locally, then upload and publish it to your Connect project.
  • A deployment targets a published runtime at a cohort of devices (optionally filtered to specific devices or tags).
  • Devices verify every update against TUF signatures before installing. How the update applies depends on what changed: extension-level changes (like the heartbeat script change in this mission) merge live with no reboot, while updates that carry a new OS image apply to A/B partitions and reboot into the new slot, with automatic rollback on boot failure. See the update architecture overview and the activation process for the exact rules.

Prerequisites​

  • The Avocado CLI installed, and optionally Avocado Desktop if you prefer a GUI for upload and deployment
  • Docker running on your machine. The CLI builds inside a container.
  • No hardware. The device in this mission is a QEMU virtual machine. To use a board instead, see Get Started for provisioning it, then follow the same steps.
Came from the Fleet card?

The Try it on a QEMU virtual device card on the Fleet page in Avocado Connect builds the qemu-quickstart project. That project has no heartbeat extension, so you can't make the Step 5 change in it. Run poweroff in that VM's console, then start at Step 1 here. You already have an account and are logged in, so skip Step 2 and avocado connect auth login in Step 3. In Step 4, expect Connect to hold the new VM as a reclaim request.

Step 1: Create the project​

Create a project from the shell-heartbeat reference. Its heartbeat extension is a shell script, run as a systemd service, that logs system vitals as JSON every 30 seconds. That makes it a good first thing to update: you can see the change land in the device's logs.

avocado init --reference shell-heartbeat my-heartbeat
cd my-heartbeat

Run every command from here on inside my-heartbeat.

Step 2: Create an Avocado Connect account​

Sign up for a free developer account at connect.peridio.com/login. For more on what Avocado Connect provides, see the Connect overview.

  • Authenticate the CLI: avocado connect auth login — opens a browser link, then select your organization.
Terminal running avocado connect auth login: a browser login URL is printed, an organization is selected, and the CLI reports a successful login.
  • The browser confirms the CLI session and issues an API token scoped to your organization; return to your terminal.
Browser confirmation page after logging in: CLI Authenticated — your Avocado CLI is now connected and an access token has been created for this session.
  • Initialize Connect in the project: avocado connect init — select the organization and project; this writes the connect: section to avocado.yaml, adds the connect extensions to the runtime, creates a claim token, and writes the device config overlay.
Terminal running avocado connect init: organization and project selection, claim token creation, and the final Connect initialized summary with org, project, server key, and claim token.
  • In Avocado Desktop, the same Connect configuration lives in the Fleet bar — pick the org, project, and cohort, then Save. The Upload and Deploy OTA actions used later in this guide live in the same place.
Avocado Desktop with the project connected to Fleet: the Configure Avocado Connect bar with org, project, and cohort dropdowns alongside Reconfigure, Upload, and Deploy OTA tabs.
info

If your device was provisioned before Connect was initialized in the project, its image contains no Connect configuration, so it cannot enroll. After running avocado connect init, rebuild the image so it picks up the Connect extensions and device config (avocado install && avocado build), then re-provision the device. It enrolls and auto-claims into your project on first boot.

Step 4: Build and boot your device​

Install the dependencies, build the runtime, and provision a QEMU disk image:

# Install the SDK toolchain, extension dependencies, and runtime packages
avocado install

# Build the extensions and assemble the runtime
avocado build

# Create the bootable disk image for the dev runtime
avocado provision dev

Boot the virtual device:

avocado sdk run -iE vm dev

Log in as root. The dev runtime has no root password. Because the project was linked to Connect in Step 3, the device enrolls and claims itself into your project on first boot.

Reusing a QEMU device

Every QEMU VM reports the same hardware ID. If you already claimed a QEMU device in your organization, for example from the Fleet card, Connect treats this one as that device coming back and holds it as a reclaim request, so it doesn't show Online yet. Approve the request from the banner on the device page or under Settings → Reclaim requests, and the device comes online.

Confirm the device is online in Connect: open your project's fleet, find the device, and check that its status shows Online.

A device detail page in Avocado Connect showing the device status as Online, with its identifier, cohort, tags, and last-seen time.

In the VM, watch the heartbeat output. Keep this terminal open: you'll see the update land here in Step 7.

journalctl -u heartbeat -f --no-pager

Each line is one JSON reading:

{ "uptime": 42, "mem_free_kb": 398212, "mem_total_kb": 524288, "load_1m": "0.01", "ts": 1740700000 }

The numbers depend on your VM. Only the field names matter here.

Step 5: Make a change and upload it​

Add a new metric, free disk space, to the heartbeat script. In heartbeat/overlay/usr/local/bin/heartbeat.sh, read the free space on /var and add it to the JSON line. The root filesystem is a read-only image, so /var, the writable data partition, is the one that changes:

heartbeat/overlay/usr/local/bin/heartbeat.sh
#!/bin/sh
set -eu

# Source configuration
INTERVAL=30
[ -f /etc/heartbeat.conf ] && . /etc/heartbeat.conf

while true; do
uptime_secs=$(awk '{print int($1)}' /proc/uptime)
mem_free_kb=$(awk '/MemFree:/ {print $2}' /proc/meminfo)
mem_total_kb=$(awk '/MemTotal:/ {print $2}' /proc/meminfo)
load_1m=$(awk '{print $1}' /proc/loadavg)
disk_free_kb=$(df -k /var | awk 'NR==2 {print $4}')
ts=$(date +%s)

printf '{"uptime":%d,"mem_free_kb":%d,"mem_total_kb":%d,"load_1m":"%s","disk_free_kb":%d,"ts":%d}\n' \
"$uptime_secs" "$mem_free_kb" "$mem_total_kb" "$load_1m" "$disk_free_kb" "$ts"

sleep "$INTERVAL"
done

In a second terminal on your host, from my-heartbeat, rebuild the runtime. The heartbeat extension has no compile step, so this only repackages the overlay files:

avocado build

Option A: Avocado CLI​

  • Upload the dev runtime as version v0.1.1 and publish it (draft → published) in one step:
avocado connect upload dev --publish --version v0.1.1
  • The CLI lists and uploads each artifact, then publishes the runtime and ends with:
[INFO] Publishing runtime...
[SUCCESS] Runtime v0.1.1 published (status: published)
  • Add --description to annotate the upload. As a fast path, the --deploy-cohort and --deploy-activate flags create and activate a deployment in the same command.
Terminal running avocado connect upload dev --publish --version v0.1.1: artifacts are discovered and uploaded with progress bars, then the runtime is uploaded and published.

Option B: Avocado Desktop​

  • In the Fleet bar, open the Upload tab, enter a version for the runtime, and click Upload.
The Upload tab in Avocado Desktop's Fleet bar: the dev runtime with a version field set to v0.1.2 and an Upload button.
  • The output panel tracks each stage of the upload — verifying build prerequisites, discovering artifacts, creating the runtime, uploading artifacts, and finalizing — and reports Ready to OTA fleet when done.
Avocado Desktop's output panel after an upload: all stages complete — verify build prerequisites, discover artifacts, create runtime, upload artifacts, finalize — with the status Ready to OTA fleet.
  • Whichever option you used, verify the upload: the runtime appears under Runtimes in the Connect console with status Published.
The Runtimes tab in Avocado Connect: the published v0.1.1 version of the dev runtime with its build ID, module count, and Deploy and Deprecate actions.

Step 6: Create a deployment​

Deployments target a cohort. The device usually has one already, because avocado connect init assigns the project's default cohort. Check the Cohort field on the device's Overview in Connect. If it's empty, use Assign next to Cohort, pick the cohort, and Save.

A device's Overview in Avocado Connect with the Cohort dropdown open: the jetson-nano cohort selected and Save and Cancel buttons alongside.

Option A: Avocado CLI​

  • Run avocado connect deploy --activate. If the project has more than one published runtime, the command asks which version to deploy. With only one, it picks that one and skips the prompt.
Terminal running avocado connect deploy --activate: an interactive list of published runtime versions to select from.
  • If there's more than one cohort, it then asks which cohort to target. With only one, it skips this prompt too.
The avocado connect deploy prompt after selecting a runtime: an interactive list of cohorts to target.
  • The CLI creates the deployment and activates it. Omit --activate to create the deployment without starting the rollout, and activate it later in Connect.
The completed avocado connect deploy run: the deployment is created and activated, with its runtime, cohort, and active status summarized.

Option B: Avocado Desktop​

  • In the Fleet bar, open the Deploy OTA tab, pick the runtime and cohort, and click Deploy OTA. A confirmation dialog reminds you which cohort you're about to update — it could affect production devices in the field. The output panel then tracks each stage: resolving the runtime and cohort, creating the deployment, and activating it.
The Deploy OTA tab in Avocado Desktop with runtime and cohort selected: a confirmation dialog asks Deploy OTA update? warning it will deploy to the jetson-nano cohort and could affect production devices, above output stages for resolving, creating, and activating the deployment.
  • The rollout is asynchronous: the device applies the update on its own schedule. An extension-only change like this one merges live, and a reboot happens only when the update carries a new OS image. Step 7 shows how to watch it land.

Option C: Avocado Connect​

  • From the Runtimes tab, click Deploy on the published runtime to open the New Deployment form: name the deployment, pick the runtime and cohort, and optionally add a description.
  • Under Advanced options, you can target specific devices or filter devices by tags.
  • Choose Create & Activate to start rolling out immediately, or Create Deployment if you wish to activate it at a later time.
The New Deployment form in Avocado Connect: deploying runtime v0.1.1 to the jetson-nano cohort with a description, advanced options, a preview of changes, and Create Deployment and Create & Activate buttons.

Step 7: Watch the update land​

In Avocado Connect​

However you created the deployment, its rollout is tracked in Avocado Connect. Open the deployment and switch to the Rollout tab to watch convergence: total devices, on-target percentage, pending and stale counts, and per-device update status.

When the rollout starts, devices in the cohort show as Pending with their current (old) runtime.

An active deployment's Rollout tab in Avocado Connect: 1 device total, 0% on target, 1 pending — the device is Online but still reports its previous runtime.

As each device applies the update, it moves to On target. When every device converges, the deployment is Completed.

A completed deployment's Rollout tab in Avocado Connect: 1 device total, 100% on target, 0 pending — the device reports the new runtime and is On target.

On the device​

Go back to the VM terminal that is following the heartbeat log. Once the device is On target, the heartbeat service restarts with the new script, and the next reading includes disk_free_kb:

{
"uptime": 542,
"mem_free_kb": 397980,
"mem_total_kb": 524288,
"load_1m": "0.02",
"disk_free_kb": 276480,
"ts": 1740700500
}

To confirm which runtime is active, run avocadoctl runtime list in the VM. The newest runtime is marked active.

You shipped a change to a running device over the air, without a reboot.

Verify it works​

  1. Your QEMU device shows Online in your project's fleet in Avocado Connect.

  2. The new runtime version appears under Runtimes in Avocado Connect with status Published.

  3. The deployment's Rollout tab shows your device On target.

  4. The heartbeat log on the device includes disk_free_kb.

Rollback and safety​

  • What happens when a bad OS update ships? A/B rollback to the rescue — read our Field Note: Two boots to trust an update.
  • For more information, see Deployments and the update architecture.
  • Turning on a manifest-level security feature (image.verity, var.encrypt) over OTA has an ordering rule: an update is applied by the avocadoctl already on the device, so ship the current avocadoctl in a plain OS update first. Security features covers which transitions work over OTA and which need a reprovision.

Finished every step above?