Deployments
A deployment is the record that pairs a specific firmware artifact with a
specific device and tracks the update from the moment the server queues it
until the device confirms success or failure. Every time you push a firmware
version to one or more devices, the server creates one FirmwareDeployment
row per device.
Targeting devices
Section titled “Targeting devices”POST /firmware/{id}/deploy always requires an explicit list of target devices:
pass device_ids with at least one device UUID. A request with an empty
device_ids list, or without the field, is rejected with HTTP 422. The server
never selects devices on its own, so one request can never reach the whole fleet
by accident.
If the firmware has min_version set, devices below that version are skipped
unless the caller passes force: true (requires owner role or above).
Phased rollout
Section titled “Phased rollout”There are two ways to limit how many devices an update reaches. Both go on the
POST /firmware/{id}/deploy request. In the console, both are options under
Rollout Strategy in the deploy dialog.
One-shot sample
Section titled “One-shot sample”Pass rollout_percentage (0–100) to limit the deployment to a random sample of
the devices you listed. For example, rollout_percentage: 10 creates
deployments for 10% of them. Run the same call again with a higher percentage to
widen the rollout. This is a one-shot sampling. It does not expand over time.
Batched rollout
Section titled “Batched rollout”Pass a rollout object to deploy in batches. The server creates one deployment
per device up front. It releases them one batch at a time and advances on its
own.
{ "device_ids": ["<device-uuid>", "<device-uuid>"], "rollout": { "batch_percent": 20, "batch_interval_s": 3600, "max_failure_percent": 10 }}| Field | Meaning |
|---|---|
batch_size | Devices per batch (integer, at least 1). |
batch_percent | Devices per batch as a percentage of the devices the server actually deploys to (greater than 0, up to 100). Rounded down to a whole number, with a minimum of one device. |
batch_interval_s | Seconds to wait before moving on. Default 0. |
max_failure_percent | Pause the rollout when more than this share of a batch has failed. Default 10. |
Set exactly one of batch_size and batch_percent. The request is rejected
with HTTP 422 if you set both or neither. The console offers only
batch_percent. It takes the interval in minutes and sends it as
batch_interval_s.
Devices are batched in the order you listed them in device_ids. The last batch
holds the remainder and can be smaller than the others. If you also pass
rollout_percentage, the random sample is taken first and only the sampled
devices are batched.
The response to the deploy call includes rollout_group_id and batch_count.
Every deployment in the rollout also carries rollout_group_id and
rollout_batch (the batch number, starting at 0).
How it advances. The first batch starts immediately. The server checks the
active rollouts about every 10 seconds. The current batch counts as done when
every deployment in it has finished (installed, failed or rolled_back).
If batch_interval_s is above 0, the batch also counts as done once that
many seconds have passed since it started, even if devices are still working.
When a batch is done, the server activates the next one. After the last batch,
the rollout is completed.
Automatic pause. When a batch is done, the server counts its failed and
rolled_back deployments. If that share is above max_failure_percent, the
rollout moves to paused and records the reason. A paused rollout never
resumes by itself.
Controlling a rollout. These calls take organization_id as a query
parameter. They need the member role or above:
| Call | Effect |
|---|---|
GET /api/rollouts/{id} | Returns state (active, paused, completed or aborted), batch_index, batch_count, params and paused_reason. |
POST /api/rollouts/{id}/pause | Stops the rollout from starting further batches. Use it on an active rollout. Devices already in flight are not touched. |
POST /api/rollouts/{id}/resume | Use it on a paused rollout. You accept the failures of the batch that paused it. The server starts the next batch, or completes the rollout if that was the last one. |
POST /api/rollouts/{id}/abort | Use it on an active or paused rollout. Deployments still scheduled become failed with the message “rollout aborted”. The batch already running finishes on its own. A completed rollout cannot be aborted (HTTP 422). |
The console shows the same state, batch progress, paused reason and Pause, Resume and Abort buttons on the Deployments tab of the Firmware page.
A paused rollout stays paused until someone resumes or aborts it. This is different from pausing a single deployment, which expires after one hour (see Pause, resume, and cancel).
Status states
Section titled “Status states”Each deployment record carries a status field. The device’s OTA agent
reports transitions back via POST /v1/ota/status/{deployment_id}.
| Status | Meaning |
|---|---|
scheduled | The deployment belongs to a later batch of a batched rollout that has not started yet. The device cannot see it. The status becomes pending when its batch starts. If the rollout is aborted first, it becomes failed. |
pending | Server created the deployment; device has not started yet. The heartbeat response will advertise an available update. |
downloading | Device acknowledged the update and is transferring the artifact. |
downloaded | Artifact transfer complete; device has the file locally but has not started writing yet. |
installing | Device is writing the artifact to the inactive partition. |
installed | Device successfully applied the update and rebooted into the new firmware. The device’s firmware_version is updated to the new version. |
failed | The update did not complete successfully. The error_message field contains a description. |
rolled_back | An operator triggered a rollback after the update reached installing, installed, or failed. |
Pause, resume, and cancel
Section titled “Pause, resume, and cancel”Operators with at least member role can intervene while a deployment is in
flight.
Pause — available from pending or downloading. The server inserts a
pause control directive that expires after 1 hour; the paused_at
timestamp is recorded. A pause is a temporary hold, not an indefinite stop:
when the hour is up the server stops sending the directive and the agent
continues the update on its own. The console shows the expected auto-resume time
next to the Paused badge. To stop an update for longer than an hour, cancel it
instead.
Resume — clears the pause directive and paused_at. The agent receives a
continue directive on its next poll.
Cancel — available at any status. Sets deployment to failed (“Cancelled
by operator”) and inserts force_cancel. Cancelling an already-failed
deployment is a no-op.
Only allowed from failed. The deployment resets to pending, retry_count
increments, and the error is cleared. Maximum five retries; further attempts
return 422.
Rollback
Section titled “Rollback”Allowed from installing, installed, or failed. The server sets
status = rolled_back and inserts a force_cancel directive. The agent
receives the directive on its next poll and uses the bootloader to revert to
the previously active partition. Already-rolled-back deployments are a no-op.
Every pause, resume, cancel, retry, and rollback action is written to the audit log with the operator’s user ID and a timestamp.
Every deployment ships an artifact — see Artifacts for the file format and signing requirements.