Deletion & retention
houba removes tags along two distinct axes, and never hard-deletes a tag without it first passing a usage gate. This page explains the model; for the runnable steps, see Purge unused tags.
Delegated deletion (deletionMode: mark)
When a tag drops out of the selection, houba does not delete it — it attaches a
pending-deletion OCI referrer (application/vnd.houba.lifecycle.pending+json, carrying
io.houba.lifecycle.marked-at / io.houba.lifecycle.reason / io.houba.lifecycle.state and the
policy/import identity). The digest is unchanged and the tag stays pullable. An external reaper
lists these referrers, checks production usage, and purges. If the tag re-enters the selection on
a later run, houba clears the mark. If deletionMode is later removed or changed to purge, the
next reconcile hard-deletes any still-undesired tags (the stale marks become moot).
Resolution is a cascade (most-specific wins): deletionMode on the policy wins, else the
destination's deletion_mode (in HOUBA_REGISTRIES), else the global HOUBA_DELETION_MODE
(default purge).
Example: pending-deletion/pending-deletion.yml.
Retention (capping valid tags)
Delegated deletion handles tags that fall out of selection. Retention handles the opposite
problem: tags that stay perfectly valid but pile up forever — a policy that mirrors every patch
(includeRegex: "^7\\.2\\.") keeps accumulating 7.2.z tags, each still in selection, so the
selection axis never touches them.
retention/redis.yml activates the archive knobs to cap them:
archive:
keep: 3 # always retain the 3 most-recently-imported 7.2.* tags
olderThanDays: 30 # of the rest, mark only those older than 30 days
During reconcile, houba ranks each stream's in-selection tags by import time (houba's own
stamp, org.opencontainers.image.created), keeps the keep newest, and attaches a
pending-deletion referrer (reason retention-excess) to any older tag beyond that count — both
conditions must hold (keep and olderThanDays). Alias targets (e.g. whatever latest points
at) are never marked, and a mark clears automatically if the tag stops being excess on a later run.
Retention only ever marks — it never hard-deletes, even under deletionMode: purge: removing a valid tag must always pass the usage gate. So retention presupposes a scheduled houba purge; without one, marks accumulate harmlessly and the tags stay fully pullable.
Thresholds cascade global ← policy, per field: a fleet-wide default in HOUBA_RETENTION
(a JSON Archive object) is refined by a policy's archive:. With neither set, retention is off
and behaviour is unchanged.
# fleet-wide default (optional); a policy's `archive:` overrides it per field
export HOUBA_RETENTION='{"keep": 5, "olderThanDays": 90}'