Skip to main content
Documentation
Preview
You're viewing the Next docs — a rolling preview of in-development changes. The current release docs may differ.

Spreading Updates

When a tag moves to a new version, every device tracking the tag installs the version on its next check. A spread releases the update to devices gradually instead, so that problems with a release surface while only a small share of the fleet has installed it. A spread can release devices automatically over a configured duration, up to a percentage that you raise manually, or both. The devices determine on their own whether they may update, so a spread needs no per-device rollout management.

Spreading is an optional feature of an integration. An integration that supports it lists spread in the capabilities of its status. Other integrations ignore the setting and install the target without waiting.

Device Positions

Each device derives a position between 0 and 1 from its device ID and the ID of the target version. A spread releases devices in the order of their positions: a device with position 0.12 may update once 12% of the fleet is released.

Positions are fixed. A device needs no stored state to know whether it may update, its eligibility does not depend on how often it checks, and Nexigon Hub can compute the schedule of every device. Because the position depends on the target version, different devices go first with each release. For a fixed group of devices that always update first, use a canary tag or a fleet operation instead.

Spreading over Time

A spread over time releases devices automatically over a duration:

dev.nexigon.ota.config
{
  "path": "acme/gateway-os/stable",
  "spread": { "durationSecs": 604800 }
}

The share of released devices grows quadratically with the elapsed time:

Elapsed TimeReleased Devices
10%1%
25%6%
50%25%
75%56%
100%100%

Few devices update early, while problems with a new release are most likely to surface, and the pace increases as the release proves itself. A device that is offline when it is released updates as soon as it reconnects.

By default, the spread starts when the tag in the configured path was assigned to its current version. Nexigon Hub records this time for every tag. In the example, moving stable to a new version starts a new seven-day spread, and the configuration does not change between releases.

An explicit startAt overrides the tag’s assignment time:

dev.nexigon.ota.config
{
  "path": "acme/gateway-os/pkg_v_3sLgP9eKd7XwHn2QaRt5Zm",
  "spread": { "startAt": "2026-10-19T06:00:00Z", "durationSecs": 259200 }
}

Use an explicit start to spread an update to a version pinned by ID, or to begin a spread at a planned time, independent of when the version was tagged. Without an explicit start, updates to versions pinned by ID are not spread over time. The explicit start also applies to later targets. If the configured tag moves after the start, devices released at that point install the new version right away.

Releasing a Percentage

A percentage releases the update to a share of devices right away. Raise it step by step to release a version manually:

dev.nexigon.ota.config
{
  "path": "acme/gateway-os/pkg_v_3sLgP9eKd7XwHn2QaRt5Zm",
  "spread": { "percentage": 5 }
}

Raising the percentage only adds devices. The devices released at 5% remain released at 20%, so you can go from 5% to 20% to 100% while observing the fleet. Fractions such as 0.5 release a share smaller than one percent.

The percentage applies to whichever version is the target. If the configured tag moves, the new version is released to the same percentage right away. Pin the version by ID for manual releases, or lower the percentage before moving the tag.

Combining Both

With both a duration and a percentage, the percentage caps the spread over time. For example, the following spread releases devices over a week, but never more than 20% until you raise the percentage:

dev.nexigon.ota.config
{
  "path": "acme/gateway-os/stable",
  "spread": { "durationSecs": 604800, "percentage": 20 }
}

When you raise the cap, devices whose turn in the spread over time has already passed update right away.

Waiting for a Turn

While a device is not released, it reports the state blocked with the code spread. If the device knows when it will be released, nextAttemptAt holds that time:

dev.nexigon.ota.status
{
  "currentVersion": "2.1",
  "state": "blocked",
  "blocked": { "code": "spread" },
  "targetVersionId": "pkg_v_3sLgP9eKd7XwHn2QaRt5Zm",
  "nextAttemptAt": "2026-10-18T21:33:52Z"
}

nextAttemptAt is absent for a device outside the released percentage, as its release depends on the next change to the percentage.

Once released, the device proceeds as usual. It waits for its maintenance window, installs any checkpoints the target requires, and retries failed attempts according to its retry policy. The spread applies to the target, so the steps through checkpoints do not wait for further releases.

Halting a Spread

To halt a spread, hold the devices. Devices that have not updated yet stay where they are, devices that have already updated keep the new version, and all of them report the code hold.

A hold does not stop the clock of a spread over time. Removing the hold releases every device whose turn passed in the meantime at once. To resume gradually instead, set a percentage below the share already released, then remove the hold and raise the percentage step by step.

To revert the release, move the tag back to the previous version and remove any hold. Devices that have already updated then install the previous version again, unless going back crosses a checkpoint. Without an explicit start, they do so over a new spread that starts with the reassignment.

Computing Eligibility

Integrations must determine whether a device is released exactly as follows, so that Nexigon Hub predicts the same schedule as the devices:

  1. Compute the SHA-256 digest of the UTF-8 string <device-id>/<target-version-id>.
  2. Read the first 8 bytes of the digest as a big-endian unsigned integer n. The position is u = floor(n / 2¹¹) / 2⁵³. It uses the top 53 bits of n, so that a double-precision number represents u exactly and 0 ≤ u < 1.
  3. With percentage, the device is released only if u < percentage / 100.
  4. With durationSecs, determine the start of the spread: startAt if set, otherwise the time at which the tag in the configured path was assigned to its current version. The tag’s assignment time is the assignedAt field of the tag in the version details. If neither exists, skip this step. Otherwise, the device is released only from the start plus floor(durationSecs × √u) seconds, computed with IEEE 754 double-precision arithmetic.

A device is released when all applicable conditions hold. For example, with a seven-day spread starting on 2026-10-12 at 08

UTC:

StepValue
Input stringd_5mKpR3vXq8TzLw2NhYc7Bf/pkg_v_3sLgP9eKd7XwHn2QaRt5Zm
First bytese1 2f 3c 23 99 59 8d 1c
n16226254106033491228
u0.8796269976531673
Delay567232 seconds
ReleasedFrom 2026-10-18T21:33:52Z

With an additional percentage, the device is released only once the percentage exceeds 87.96.