Install the OTA-Pulse Agent
The OTA-Pulse agent (soc-ota-agent) is the device-side component that polls the backend for pending deployments, downloads firmware artifacts, writes them to the inactive A/B partition, and reboots into the new image. It runs as a systemd service and is written in Go.
Three installation paths are supported:
| Method | When to use |
|---|---|
Yocto layer (meta-otapulse) | Greenfield or existing Yocto builds — the recommended production path |
Buildroot external tree (buildroot-otapulse) | Buildroot-based boards |
| Manual binary install | Quick device tests, development VMs, or any Linux device |
Yocto (meta-otapulse)
Section titled “Yocto (meta-otapulse)”The meta-otapulse layer bakes the agent, its config directory, systemd unit, and identity/inventory scripts directly into your image.
Supported releases: Kirkstone (LTS) · Langdale · Mickledore · Nanbield · Scarthgap
-
Clone and add the layer
Terminal window cd /path/to/yocto/sourcesgit clone https://github.com/binaryblack/OTA-Pulse.git ota-pulse# Add to your buildbitbake-layers add-layer /path/to/ota-pulse/meta-otapulseOr add the path directly to
conf/bblayers.conf:BBLAYERS += "${BSPDIR}/sources/ota-pulse/meta-otapulse" -
Inherit the integration class in your image recipe
For standard images (rootfs < 1 GB):
# In your image recipe (e.g., my-image.bb)inherit otapulseFor large images (Qt, multimedia — rootfs > 1 GB):
inherit otapulse-largeWhat
inherit otapulsedoes automatically:- Adds
soc-ota-agentto the image - Enables
.otapulseartifact generation - Adds
ext4toIMAGE_FSTYPES - Includes partition tools (
gptfdisk)
- Adds
-
Set the OTA server URL and a provisioning token in
conf/local.conf# Required: your OTA-Pulse backend URL. It must be https://.# By default the build fails on plain http://. OTAPULSE_ALLOW_INSECURE_URL = "1"# overrides that for isolated dev setups only.OTA_SERVER_URL = "https://<your-server>"# Recommended: a fleet provisioning token (spt_...) from Provisioning in the console.# Here it satisfies the build-time credential check.OTAPULSE_PROVISIONING_TOKEN = "spt_..."# Optional: enable artifact signature verification for productionSOC_OTA_SIGNATURE_VERIFICATION = "1"SOC_OTA_VERIFY_KEY_FILES = "production-rsa-public.pem production-ecdsa-public.pem"The device registers itself on first boot with the same token. Place it on the device in
/etc/otapulse/provision.env(mode 0600):Terminal window OTAPULSE_PROVISIONING_TOKEN=spt_...No recipe writes
provision.envfor you, so your image or provisioning process must place it. See Register your first device for what happens next. -
Build the image
Terminal window bitbake your-image# Output: tmp/deploy/images/<MACHINE>/your-image-<MACHINE>.otapulse -
Flash and verify
Terminal window # Flash the WIC imagesudo dd if=your-image-<MACHINE>.wic of=/dev/sdX bs=4M status=progress# After first boot, verify on the devicesystemctl status soc-ota-agentsoc-ota-agent show-artifact
Signing keys for production
Section titled “Signing keys for production”The layer ships with placeholder development keys. For production you must supply your own keys.
Option A — Replace the example keys:
cp your-production-rsa-public.pem \ meta-otapulse/recipes-core/signing-keys/files/example-rsa-public.pemOption B — Point to your keys in local.conf:
SOC_OTA_VERIFICATION_KEYS = "/absolute/path/to/production-rsa-public.pem"Supported algorithms: RSA-4096 PSS/SHA-256 and ECDSA-P384 SHA-384.
Buildroot (buildroot-otapulse)
Section titled “Buildroot (buildroot-otapulse)”-
Configure Buildroot with the OTA-Pulse external tree
Terminal window cd /path/to/buildrootmake BR2_EXTERNAL=/path/to/OTA-Pulse/buildroot-otapulse menuconfig -
Enable the package
In the
menuconfiginterface navigate to:Target packages → OTA-Pulse → otapulseConfigure the required settings:
- OTA Server URL —
https://URL of your OTA-Pulse backend - Artifact Verification Public Key Path — path to your RSA-4096 public key (mandatory)
- Device Type Identifier — stable identifier for this board
- OTA Server URL —
-
Generate signing keys (required)
Signature verification is mandatory in the Buildroot package. Generate a key pair before building:
Terminal window # Generate RSA-4096 key pairopenssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:4096openssl rsa -pubout -in private.pem -out public.pemKeep
private.pemin your CI/CD secrets. Setpublic.pemas the value ofBR2_PACKAGE_OTAPULSE_VERIFY_KEY. -
Build
Terminal window makeExample defconfigs are available for quick starts:
Config Platform Status otapulse_qemu_aarch64_defconfigQEMU (testing) Validated on QEMU only. A development image: it sets a fixed root password. otapulse_rpi4_defconfigRaspberry Pi 4 Starting point. Not validated on hardware. otapulse_imx8_defconfigNXP i.MX8 Starting point. Not validated on hardware. The Buildroot package does not include the bootloader-level revert that the Yocto layer provides on some boards. See Platform support and the board table in Yocto integration.
Manual binary install
Section titled “Manual binary install”Use this method to install the agent on any running Linux device over SSH — useful for development VMs, boards already in the field, or testing without a full Yocto/Buildroot workflow.
Prerequisites
Section titled “Prerequisites”- Go 1.21+, GCC,
liblzma-dev,libssl-dev,libglib2.0-dev(build host only) - Target device: Linux with systemd, kernel 4.x+, A/B root partition layout
-
Build the agent on your host
Terminal window cd /path/to/OTA-Pulse/soc-ota-agentmake buildThis produces an
otapulsebinary. For cross-compilation:Terminal window GOOS=linux GOARCH=arm64 make build -
Copy the binary to the device
Terminal window scp otapulse user@<device-ip>:/tmp/ssh user@<device-ip> \"sudo install -m 0755 /tmp/otapulse /usr/bin/soc-ota-agent" -
Create the configuration directory and config file
Terminal window ssh user@<device-ip> "sudo mkdir -p /etc/otapulse"Place the following JSON at
/etc/otapulse/otapulse.confon the device (fill in your values):{"ServerURL": "https://<your-server>","UseSoCMonitoring": true,"SoCAPIKey": "<device-api-key>","SoCDeviceID": "<your-device-id>","UpdatePollIntervalSeconds": 1800,"InventoryPollIntervalSeconds": 28800,"RetryPollIntervalSeconds": 300}Field Description ServerURLThe bare https://URL of the OTA-Pulse backendUseSoCMonitoringtruemakes the agent authenticate withSoCAPIKeyandSoCDeviceID. The Yocto image sets it the same way.SoCAPIKeyA device API key from API Keys in the console sidebar SoCDeviceIDA stable unique identifier for this device, such as a MAC address or hardware serial A manual install has no first-boot provisioning service, so you supply the API key yourself. Images built with the Yocto layer register themselves with a provisioning token instead (see the Yocto tab).
-
Install and start the systemd service
Terminal window scp /path/to/OTA-Pulse/soc-ota-agent/support/soc-ota-agent.service \user@<device-ip>:/tmp/ssh user@<device-ip> \"sudo cp /tmp/soc-ota-agent.service /etc/systemd/system/ && \sudo systemctl daemon-reload && \sudo systemctl enable soc-ota-agent && \sudo systemctl start soc-ota-agent" -
Verify the agent is running
Terminal window # On the devicesystemctl status soc-ota-agentsoc-ota-agent show-artifactjournalctl -u soc-ota-agent -f
File locations
Section titled “File locations”After installation, the agent uses the following paths regardless of install method:
| Path | Description |
|---|---|
/usr/bin/soc-ota-agent | Agent binary |
/etc/otapulse/otapulse.conf | Main configuration (JSON). On image-based devices this file belongs to the root filesystem. |
/var/lib/otapulse/otapulse.conf | Second configuration file, read first. See Configuration files. On Yocto images the agent service sets OTAPULSE_DATASTORE_DIR=/data/otapulse, so the file is /data/otapulse/otapulse.conf on the data partition. |
/etc/otapulse/scripts/ | State scripts (update lifecycle hooks) |
/var/lib/otapulse/ | Runtime state and deployment log |
/usr/share/otapulse/identity/ | Device identity scripts |
/usr/share/otapulse/inventory/ | Inventory collection scripts |
Configuration files
Section titled “Configuration files”The agent reads two configuration files. It loads the persistent file first and
/etc/otapulse/otapulse.conf second. When both set the same field, the /etc
file wins.
On a device built from a Yocto or Buildroot image, /etc/otapulse/otapulse.conf
is part of the root filesystem. Each image carries its own copy, with the
ServerURL that was set when the image was built. An A/B update boots the other
slot, so after the switch the agent reads the new image’s copy. An edit you make
to /etc/otapulse/otapulse.conf on a running device is lost at the next slot
switch. A ServerURL in the persistent file cannot override the /etc value
either, because the image’s /etc file sets it. To change the server for good, build
the image and the update artifacts with the new server URL (OTA_SERVER_URL in
Yocto).
On Yocto images, first-boot provisioning writes the device credentials
(SoCAPIKey and SoCDeviceID) to the persistent file. It lives on the data
partition, which survives updates. A SoCAPIKey in the /etc file would take
precedence over it.
Quick troubleshooting
Section titled “Quick troubleshooting”| Symptom | Check |
|---|---|
| Service won’t start | sudo journalctl -u soc-ota-agent --no-pager — look for config parse errors |
| Cannot reach backend | curl https://<your-server>/health from the device |
| Authentication failures | Check UseSoCMonitoring, SoCDeviceID and the ServerURL in the config files (see Agent registers but never appears); check system clock (NTP) |
| State stuck | cat /var/lib/otapulse/deployment.log |
Next: Register your first device
Section titled “Next: Register your first device”With the agent installed and running, register it in the console.