Rollouts
A rollout deploys a distribution set across many targets in stages, advancing from one group to the next only when success thresholds are met — so a bad update is caught on a small group before it reaches the whole fleet.
How it works
- You create a rollout from a FIQL target filter, a distribution set, and a number of groups. Matching targets are split evenly across the groups at creation time.
- Each group has a success threshold and an error threshold (percentages).
- Starting the rollout schedules the first group only — its targets get the DS assigned.
- A background evaluator watches each running group:
- When the error threshold is reached, the group and rollout pause.
- When the success threshold is reached, the group finishes and the next group is scheduled.
- When the last group finishes, the rollout is finished.
Creating a rollout
curl -u admin:pw -X POST localhost:8088/rest/v1/rollouts \
-H 'Content-Type: application/json' \
-d '{
"name": "fleet-1.1",
"distributionSetId": 1,
"targetFilterQuery": "controllerId==device-*",
"amountGroups": 3,
"successCondition": {"condition":"THRESHOLD","expression":"90"},
"errorCondition": {"condition":"THRESHOLD","expression":"20"}
}'
amountGroupssplits matching targets into that many groups.successCondition.expression/errorCondition.expressionare percentages (0–100). IferrorConditionis omitted, the error threshold never trips.typeis the action type every action the rollout creates inherits —forced(default),soft,timeforcedordownloadonly— withforcetimealongside it fortimeforced. A staged download-then-install is therefore adownloadonlyrollout followed by aforcedone over the same filter. An unknown type is rejected with400.
The rollout starts in ready — or in waiting_for_approval when the
approval gate is on.
Lifecycle operations
curl -u admin:pw -X POST localhost:8088/rest/v1/rollouts/1/start
curl -u admin:pw -X POST localhost:8088/rest/v1/rollouts/1/pause
curl -u admin:pw -X POST localhost:8088/rest/v1/rollouts/1/resume
curl -u admin:pw -X POST localhost:8088/rest/v1/rollouts/1/stop
curl -u admin:pw -X DELETE localhost:8088/rest/v1/rollouts/1
- start —
ready→running; schedules the first group. - pause —
running→paused; the evaluator ignores paused rollouts. - resume —
paused→running; re-evaluates immediately. - stop — any non-terminal status →
stopping→stopped; see below. - delete — cancels any active actions in the rollout and removes it.
Stopping a rollout
Pause only stops raptor from scheduling more groups — the updates already sent out keep running on the devices that have them. Stop is the abort: it cancels those in-flight updates as well, and is terminal (a stopped rollout cannot be resumed; create a new one).
The cancellation is soft, so devices are told rather than cut off:
- Every active action the rollout issued moves to
cancelingand is served to the device ascancelActionon its next poll. Groups that had not finished are markedstopped; ones that already finished keep their outcome. - The rollout reports
stoppingwhile those cancels are outstanding. - As each device acknowledges over DDI, its action becomes
canceled. Once none are left active the rollout settles tostopped.
A rollout with nothing left in flight — every device it reached is already
finished — goes straight to stopped.
A device that is offline holds the rollout in stopping until it polls again.
That is the honest state: the update has not been called off out in the fleet
yet. To close one out without waiting, force-cancel its action
(DELETE /rest/v1/targets/{cid}/actions/{aid}?force=true).
Stop is accepted from any status that is not already terminal or draining —
ready, waiting_for_approval, approval_denied, running and paused,
matching hawkBit’s ROLLOUT_STATUS_STOPPABLE. A rollout that never started has
no actions to cancel, but stopping it is how you retire it while keeping the
record; deleting it throws that record away. It is rejected with 400 from
stopping, stopped and finished, so a second stop is an error.
Approval workflow
By default a rollout is created ready and an operator can start it straight
away. Set rollout_approval_enabled = true to put a second pair of eyes in
front of that:
rollout_approval_enabled = true
A rollout created with the gate on lands in waiting_for_approval instead.
start on it is refused until someone decides:
# Approve — the rollout moves to `ready` and can now be started.
curl -u admin:pw -X POST \
"localhost:8088/rest/v1/rollouts/1/approve?remark=checked+with+ops"
# Or deny it, permanently.
curl -u admin:pw -X POST \
"localhost:8088/rest/v1/rollouts/1/deny?remark=fleet+is+frozen"
Both take an optional remark query parameter and answer 204 No Content, so
re-read the rollout to see the outcome. The decision is reported on the rollout
as approveDecidedBy and approvalRemark — the asymmetric spelling is
hawkBit’s own, and raptor matches it.
Denial is terminal: approval_denied is not a startable status and nothing
transitions out of it, so a denied rollout can only be deleted. There is no
“undeny” — create a fresh rollout instead. Because raptor authenticates a
single operator account, approveDecidedBy is always that account’s username.
The flag is reported to clients as hawkBit’s rollout.approval.enabled tenant
config key on GET /rest/v1/system/configs.
Inspecting groups
# deploy groups with per-group status and target counts
curl -u admin:pw localhost:8088/rest/v1/rollouts/1/deploygroups
# one group
curl -u admin:pw localhost:8088/rest/v1/rollouts/1/deploygroups/5
# the controllerIds in a group
curl -u admin:pw localhost:8088/rest/v1/rollouts/1/deploygroups/5/targets
Tracking progress
Rollouts and groups both carry totalTargetsPerStatus, hawkBit’s breakdown of
their targets by deployment outcome:
{
"id": 1, "name": "fleet-1.1", "status": "running", "totalTargets": 9,
"totalTargetsPerStatus": {
"notstarted": 0, "scheduled": 3, "running": 3,
"error": 1, "finished": 2, "cancelled": 0
}
}
notstarted— the rollout has not been started, so nothing is deployed yet.scheduled— the group is waiting its turn; raptor creates actions only when a group is scheduled, so these targets have no action yet.running— an action is in flight (includingcancelingand, with the confirmation flow on,wait_for_confirmation).finished/error/cancelled— the action’s terminal state.
A rollout’s counts are the sum of its groups’. The web console renders both as progress bars — see the Web Console guide.
Evaluator cadence
The background evaluator runs every rollout_eval_interval_secs seconds
(default 5). Lower it for snappier progression in testing, raise it to reduce
load on large fleets. See the
Configuration Reference.
Dynamic rollouts
By default a rollout’s membership is a snapshot: the targets matching the filter at creation time, and no others. A device that registers an hour later is not part of it, however well it matches.
Setting dynamic: true appends a trailing dynamic group that keeps
absorbing targets as they start matching:
curl -u admin:pw -X POST localhost:8088/rest/v1/rollouts \
-H 'Content-Type: application/json' \
-d '{
"name": "fleet-1.1",
"distributionSetId": 1,
"targetFilterQuery": "controllerId==device-*",
"amountGroups": 3,
"successCondition": {"condition":"THRESHOLD","expression":"90"},
"dynamic": true,
"dynamicGroupTemplate": {"nameSuffix": "-dynamic", "targetCount": 20}
}'
That creates group-1, group-2, group-3 as usual plus group-4-dynamic,
which is empty at first. The static groups run in order exactly as before; when
the last of them finishes, the dynamic group starts and begins taking in
newcomers. A target absorbed while the group is running is deployed to
immediately, on the same action type and forced time as every other target of
the rollout.
dynamicGroupTemplate is optional and only allowed when dynamic is true
(otherwise the request is rejected, rather than silently ignored):
| Field | Meaning | Default |
|---|---|---|
targetCount | how many targets one dynamic group takes before the next is opened | the size of the last static group |
nameSuffix | appended to the generated group-<n> name | none |
What to expect
- A dynamic rollout never finishes on its own. There may always be another
device about to match, so the trailing group stays
runningno matter how many of its targets succeed. Ending one is an operator action:POST /rest/v1/rollouts/{id}/stop, orraptorctl— see below. - Groups do not reopen. Newcomers only ever land in the trailing group; a group that has finished stays finished.
- Full groups roll over. Once the trailing group holds
targetCounttargets, a new dynamic group opens behind it and the full one completes on its own thresholds. Numbering and the name suffix carry on (group-4-dynamic,group-5-dynamic, …), up to themax_rollout_groups_per_rolloutquota — at which point absorbing stops and a warning is logged. - Thresholds are measured against the group’s capacity, not against however many targets have landed in it so far. Otherwise a single early success would cross a 50% threshold in a group of one.
- Absorbing stops if the distribution set is invalidated. Withdrawing a
release with
ds invalidatekeeps it from being drawn onto further devices; usecancelRolloutsto stop the rollout itself as well. - A target that cannot take the set — an incompatible target type — is skipped with a warning rather than failing the sweep.
Absorbing starts as soon as the rollout does, not when the trailing group’s turn
comes: while the static groups ahead of it are still running, newcomers join the
dynamic group and count towards its totalTargets, but no action is issued
until the group itself starts. That is the same deal a target in group-3 gets
while group-1 is running.