Each weekly run drains Docker02 then Docker03, and the swarm never moves a
task back by itself, so every run ended with nearly every service on one
node. On 2026-10-25 the operator had that spread out by hand.
- Agent 1.26.0 reports each swarm service's pinned digest, and every
repo@sha256 its host holds (from /system/df, every 10 min).
- Once a run's last host is back in service, Steward picks services on the
fullest node to move to the emptiest until they are within one task. It
takes services with several replicas on one node first. A service moves only
when the emptier node already holds its exact image. One the plan keeps in
place, one below its replicas, and one with no pinned digest stay put, and
the run says why.
- A swarm manager's helper carries it out through a new action, rebalance,
with the service names (validated, at most 48). It force-updates each in
turn with the spec unchanged and no registry lookup, skips any a
constraint places, and gives each 5 min to settle.
- The run ends done with what moved, or failed if a move didn't settle or
the manager didn't answer in 60 min. A docker-scoped window covers the
restarts, and the step goes on the timeline.
- Plans gain "Spread swarm services back out" (on by default) and "Keep in
place", on the create form and in an "After the run" form on each plan.
list_maintenance_plans shows both and the latest run's spread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host agent writes each maintenance job once it ends, and each stretch a
host was away (a reboot, a crash, lost contact, the agent restarting), kept up
to date until it is back. Docker judges each snapshot: a unit on a new image,
or a container recreated, is a deploy; deaths past the Thresholds crash-loop
count are one crash-loop entry that later deaths extend; single restarts are
recorded but hidden. An anomaly has one entry whose type follows its review:
only those judged real are shown by default.
A source whose record changes keeps one entry for it (record_or_update).
Plugins register the records their entries point to, so an aged-out job or
downtime reads "Details no longer kept". The first fill runs once per source
(timeline.filled), the host agent's jobs and downtime included.
get_anomaly's "what else was going on" reads the timeline for the host or
resource instead of querying alerts, runs, maintenance, downtime and container
deaths itself; Docker's own source keeps only starts, stops and health
changes. A new kind, Observed, is for what Steward saw happen with no rule or
person behind it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>