Agent Updates
Overview
The Durantic agent runs on every machine you manage, and it updates itself. This page covers how you control which machines get a new agent, when, and — the part most of this page is about — what to do when a new agent turns out to be bad.
Agent updates are staged along one axis only: Environments, the same ordered promotion tiers config releases use. They are deliberately not staged along Rings.
Rings partition blast radius and converge in parallel — that is what they are for. Parallel is the right shape for config, which reports its own failures: a ring's breaker counts apply failures and trips. An agent that its own update has broken cannot report anything, so parallel tiers with a failure-counting breaker would be counting a signal that, in exactly the case that matters, never arrives. Environments are ordered and gated on evidence of health, which is the shape this needs.
The Three Levers, In Order
| Lever | Scope | What it does |
|---|---|---|
Account.auto_update_enabled |
fleet | Master switch. Off means no machine updates itself. |
Machine.auto_update |
machine | Per-machine override of the above. |
Account.agent_updates_gated |
fleet | Whether a machine may be offered the deployment's version when its environment names none. |
agent_updates_gated is the one worth reading twice.
Off (the default): a machine whose environment names no agent version — including a machine in no environment at all — is offered the version the control-plane deployment blesses. This is the behaviour that existed before staged rollouts, and it is the default so that turning staging on changes nothing until you ask it to.
On: such a machine is offered nothing. The deployment's version stops reaching your fleet, and the only way a machine gets a new agent is that its environment names one. That is the point of the flag.
Turn it on only once every machine is in an environment
agent_updates_gated: true is a silent freeze for any machine that is not in an
environment. Nothing errors, nothing warns; those machines simply stop being offered
updates. Check for unplaced machines first:
A silent freeze is the failure mode slowest to diagnose, which is why this is a flag you turn on deliberately rather than a default.
The gate does not touch the manual path.
POST /api/provisioning/machines/{uuid}/agent-update works regardless — that pairing
(auto-update off, machines moved one at a time) is the supported way to roll a fleet
that has no environments to stage across.
Running a Staged Rollout
The rollout walks your environments in promotion_order. In each tier it writes the
tier's agent_version, waits for the machines there to take it and stay healthy, and
then moves to the next tier. One rollout per account at a time — a second start gets a
409.
Starting is refused if the version is not one this control plane can serve. An agent
release declares a min-controlplane-version floor, and sending your fleet onto an agent
whose protocol the control plane cannot speak is precisely the failure that check exists
for — so it happens once, at start, rather than machine by machine.
Watching it
Per tier, you get the target, whether the cursor is there, and counts for every category the promotion decision itself uses:
| Category | Meaning |
|---|---|
on_target |
Running the target, past its soak window. |
soaking |
Running the target, not long enough yet to count as evidence. |
outstanding |
Not on the target yet. The tier holds while any remain. |
ahead |
On a newer version than the target. |
unserveable |
Never reported a version, or already stranded. |
excluded |
Cannot be moved right now — a machine mid-rebuild is the usual one. |
newly_silent |
Went quiet while this tier held it. Halt evidence. |
stale_silent |
Was already quiet when the rollout arrived. Not evidence. |
transitioned_off_target |
Took the target and is now running something else. Halt evidence. |
These numbers are the promotion gate's own output, not a summary computed alongside it. If the report says a tier is holding on two outstanding machines, that is the same sentence the engine is acting on.
hold_reason is stored rather than computed, deliberately: it is how you tell "the
rollout is waiting for something" apart from "nothing is evaluating this rollout at all".
The two halt signals, and why they are these two
A rollout halts on newly_silent or transitioned_off_target — liveness and version.
Never on a reported failure, because the failure this exists to catch is the one the
agent cannot report: a bad binary that will not start, or starts and cannot reach the
control plane. The agent's own rollback marks the attempt failed in memory and exits;
the restarted old binary reports idle, and the control plane sees no failure at all. The
only trace on the wire is that the machine went quiet, or came back on a different
version.
Three Things To Know Before You Need Them
1. You cannot roll back by starting a rollout to an older version
The agent does not downgrade itself. Start a rollout to an older build and machines
already above it stay where they are — they are reported as ahead, and the rollout
completes having moved nothing.
The rollback procedure is two calls:
PATCH /api/config/environments/{uuid} {"agent_version": "<the good build>"}
POST /api/config/environments/{uuid}/agent-updates/force {"allow_downgrade": true}
force reaches online machines only. The command rides the agent's event stream, so
a machine that is offline cannot be sent anything — and a machine the bad binary took
down is, by definition, offline. It comes back on the bad binary. Its remedy is a
rebuild. The response names every machine it did not reach, one hostname at a time,
rather than a count that hides them:
{
"target_version": "2026.09.03.132052",
"sent": ["node-a", "node-b"],
"skipped_offline": ["node-c"],
"skipped_already_on_target": [],
"skipped_would_downgrade": []
}
node-c in that response is the machine you now go and rebuild.
force ignores the tier's agent_updates_paused on purpose: a paused tier is the state
you are trying to get out of, and a lever that respects the stop it exists to override is
not a lever.
2. Do not rebuild machines in the cursor tier during a rollout
A machine mid-rebuild is excluded from the gate — it takes itself away and brings
itself back on its own schedule, so counting it as evidence would let a routine rebuild
halt your rollout. But there is a real window between issuing the reboot and the control
plane observing needs_provisioning, and in that window a rebuilding machine looks like
a machine that went quiet.
Rebuild in a tier the cursor is not in, or halt the rollout first.
3. After a halt, ahead machines are expected
A halt reverts the tier to its previous version (or, if there is none to revert to,
pauses the tier). Machines that already took the halted version are now running something
newer than the tier's target — that is what ahead means, and it is the correct state,
not a fault. They are excluded from the gate rather than blocking it, which is what lets
the tier converge again once you have decided what to do with them.
If you want them moved down, that is the rollback procedure above.
Stopping Things
| Call | Stops |
|---|---|
POST /api/config/agent-rollouts/{uuid}/halt |
Promotion. Tiers keep the version they hold. |
POST /api/config/environments/{uuid}/agent-updates/pause |
Offering any version to one tier. |
Account.auto_update_enabled = false |
Every machine updating itself, fleet-wide. |
Two notes that have cost people time:
Halting is not a rollback and does not become one. Every tier keeps the version it already holds, and machines below it keep converging up to it. Halting stops the walk.
The agent-update pause is not the config-management pause. agent-updates/pause and
/pause are separate breakers on the same environment, deliberately: a tier can be
converging config perfectly well and still be the wrong place to put a new binary.
Conflating them would mean stopping an agent rollout also stops config convergence
across the tier.
Resuming
Picks up from where the cursor stands. It does not re-stamp when the cursor entered the tier — the soak and silence windows are measured from that moment, and re-stamping would turn a machine that has gone quiet into one that has merely not been seen yet, so the halt would not fire again on the same evidence.
It also does not unpause a tier. A halt with nothing to revert to pauses the tier
instead of blanking its target — blanking would re-inherit the deployment's version,
which on a first rollout is the very version being halted. So check the tier's
agent_version and agent_updates_paused before resuming, or the walk will hold rather
than move.
Overriding One Tier By Hand
PATCH /api/config/environments/{uuid}
{"agent_version": "2026.09.05.220731", "agent_updates_paused": true}
Both fields in one call, deliberately. Setting the version and then pausing as two requests leaves a window in which the rollout engine can run and overwrite the first, and the engine refuses to write a paused tier — so the pair is what makes a manual override hold.
Setting agent_version here does the same bookkeeping the engine does: the current value
rolls into agent_version_previous and agent_version_set_at is stamped. That is what a
later halt reverts to, so a tier moved by hand has a way back just as one moved by the
engine does.