feat(updater): clean up superseded images after a stepped upgrade
The live Nextcloud 31→34 climb left 4.4 GB of images behind — one per rung, each ~1.5 GB, all still present after it finished. On a small VPS that is the difference between working and full. `system reclaim` cannot help: it collects DANGLING images, and every rung is a real tag, so all of them stay tagged and stay on disk. (Rolling apps never hit this — moving a floating tag orphans the old image, which reclaim then collects. It is specific to laddering.) After a SUCCESSFUL climb only, remove the images stepped through, keeping the immediately-previous version so a roll-back needs no download. CFG_UPDATER_UPGRADE_PRUNE=false keeps everything. Never runs on failure, where the older images are exactly what recovery may need. Tested: a 3-rung climb removes 31 and 32 and keeps 33; a single-step climb removes nothing (its previous version IS the rollback target); the config switch disables it. Found by looking at the box after the first real ladder run — the feature worked, and then quietly cost 4.4 GB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
98b7f7dd39
commit
fc169e7a4a
@ -6,4 +6,5 @@ CFG_UPDATER_SCAN_INTERVAL=30 # App Scan Interval -
|
||||
CFG_UPDATER_REGISTRY_INTERVAL=360 # Registry Check Interval - Minutes between registry lookups for new image builds (the expensive step; the local scan still refreshes every scan). 0 = only when you press Check now.
|
||||
CFG_HOTFIX_AUTO=security-breakage # Hotfix Auto-Apply - Which signed hotfix severities apply automatically on the update check [security-breakage|all|off]
|
||||
CFG_UPDATER_AUTO=true # Automatic App Updates - Master switch for per-app automatic updates. Each app's own Update Type decides individually; turning this off makes every app manual. Every update snapshots the app first and rolls back on failure. [true:On|false:Off]
|
||||
CFG_UPDATER_UPGRADE_PRUNE=true # Clean Up After Version Upgrades - After a successful stepped upgrade (e.g. Nextcloud 31→34), delete the images it stepped through. Each one can be well over a gigabyte and nothing else removes them, since every step is a real tag rather than a dangling image. The version immediately before the current one is always kept so a roll-back needs no download. [true:On|false:Off]
|
||||
CFG_UPDATER_WINDOW=06:00-08:00 # Automatic Update Window - When automatic updates are allowed to install, in the host's local time (HH:MM-HH:MM; crosses midnight when start > end; 'always' = any time). Checks still run all day so the Updates page stays current — found updates simply wait for the window. Pressing Update yourself always works.
|
||||
|
||||
@ -158,10 +158,48 @@ updaterUpgradeApp() {
|
||||
done
|
||||
|
||||
isSuccessful "$app upgraded through ${done_n} version(s) — now on $from, verified."
|
||||
_updaterUpgradePruneImages "$app" "$cur" "${rungs[@]}"
|
||||
webuiUpdaterScan >/dev/null 2>&1 || true
|
||||
return 0
|
||||
}
|
||||
|
||||
# Drop the images the climb left behind. A 3-rung Nextcloud upgrade downloads
|
||||
# ~1.5 GB per rung and keeps every one — 4.4 GB of superseded images after a
|
||||
# single upgrade, which on a small VPS is the difference between working and
|
||||
# full. `system reclaim` cannot help: it collects DANGLING images, and each rung
|
||||
# is a distinct tag, so they are all still tagged and all still there. (Rolling
|
||||
# apps do not have this problem — moving a floating tag orphans the old image,
|
||||
# which reclaim then collects.)
|
||||
#
|
||||
# Only ever after a SUCCESSFUL climb, and the immediately-previous version is
|
||||
# KEPT as the roll-back target so recovery does not depend on the network.
|
||||
# CFG_UPDATER_UPGRADE_PRUNE=false to keep everything.
|
||||
_updaterUpgradePruneImages() {
|
||||
local app="$1" start="$2"; shift 2
|
||||
local -a climbed=("$@")
|
||||
[[ "${CFG_UPDATER_UPGRADE_PRUNE:-true}" == "true" ]] || { isNotice "Keeping superseded images (CFG_UPDATER_UPGRADE_PRUNE=false)."; return 0; }
|
||||
(( ${#climbed[@]} >= 2 )) || return 0 # one step: previous IS the rollback target
|
||||
|
||||
local anchor repo
|
||||
anchor="$(updaterPrimaryImage "$app" "${containers_dir%/}/$app/docker-compose.yml")"
|
||||
repo="$(updaterRepoTag "$anchor")"; repo="${repo%:*}"
|
||||
|
||||
# Everything we moved off, minus the last one (kept for rollback).
|
||||
local -a superseded=("$start" "${climbed[@]:0:$(( ${#climbed[@]} - 1 ))}")
|
||||
unset 'superseded[-1]'
|
||||
(( ${#superseded[@]} > 0 )) || return 0
|
||||
|
||||
local tag removed=0
|
||||
for tag in "${superseded[@]}"; do
|
||||
[[ -n "$tag" ]] || continue
|
||||
if runFileOp docker image rm "${repo}:${tag}" >/dev/null 2>&1; then
|
||||
removed=$((removed + 1))
|
||||
fi
|
||||
done
|
||||
(( removed > 0 )) && isSuccessful "Removed $removed superseded image(s); kept ${climbed[-2]} for roll-back."
|
||||
return 0
|
||||
}
|
||||
|
||||
# Undo one failed rung: put the version back, restore the snapshot taken moments
|
||||
# ago, start it, and record what happened. Best effort by nature — if the
|
||||
# restore itself fails the user is told plainly rather than reassured.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user