v3.3.1 31f1eec

images.md RawHistoryFolder

Images Configuration

Configure automatic image cleanup for Docker runtime images and local registry storage.

Overview

When enabled, Gordon runs a scheduled image prune job that:

  • Prunes dangling runtime images that carry positive Gordon provenance.
  • Applies tag retention to registry repositories.
  • Preserves the latest tag.
  • Removes unreferenced blobs after tag cleanup.
  • Reports every protected and unknown candidate with its reason.

The CLI gordon images prune runs the same use case and planner as the scheduled job, with the same defaults (keep latest + 3 previous tags, both scopes enabled: dangling runtime images and registry tag retention). App state existing on the server is normal and never disables pruning; the plan decides per candidate.

Configuration

[images]
# Docker Hub, ghcr.io, quay.io, and Gordon's registry are already allowed.
allowed_registries = ["registry.internal:5000"]
require_digest = false

[images.prune]
enabled = false
schedule = "daily"
keep_last = 3

Settings

Key Type Default Description
images.allowed_registries array [] Additional registry hostname+port entries. Docker Hub (docker.io, canonical pull host registry-1.docker.io), ghcr.io, quay.io, and Gordon's configured registry are always allowed. Include non-default ports, e.g. "registry.example.com:5000". Hostnames are case-insensitive; one trailing dot and port 443 are canonicalized. This setting does not configure registry credentials.
images.require_digest bool false Require every image reference, including Gordon registry images, to use a valid @sha256:<64 hex chars> digest.
images.prune.enabled bool false Enables scheduled image cleanup
images.prune.schedule string "daily" Schedule preset: hourly, daily, weekly, monthly
images.prune.keep_last int 3 Number of newest non-latest tags kept per repository during registry cleanup (latest is always kept when present)

The policy validates registry names and strict SHA-256 digest syntax at manifest apply, digest resolution, deployment preflight, and immediately before each pull. It rejects malformed, userinfo-bearing, and ambiguous authorities. External app images currently require digest-pinned references, independently of require_digest; external tag resolution is not available. External pulls are anonymous, so adding a host to allowed_registries does not enable authenticated private-registry access. This hostname allowlist does not prove that DNS resolves to a public address and does not constrain runtime egress; enforce destination-level restrictions in the host firewall or runtime network policy.

Retention Behavior

  • latest is always preserved.
  • keep_last applies per repository and counts non-latest tags.
  • keep_last sets a minimum retained set, not a maximum: a tag named by durable app state, or referenced by the OCI closure of a protected manifest, survives beyond the window.
  • keep_last = 0 skips registry tag/blob cleanup entirely (runtime dangling prune still runs).
  • Negative keep_last values are invalid.

Prune Safety

A prune deletes only resources it can positively prove safe. Each candidate gets one verdict: eligible (deleted), protected (a durable fact claims it), or unknown (a required fact could not be read). Protected and unknown candidates are reported and left in place; an operation that deletes nothing is a success.

Durable protection covers the DESIRED and ACTIVE state of every app, including stopped and partially converged services; recovery inhibitions; staged and committed apply intents; unfinished operation journal entries; runtime container use for running and stopped containers; recent uploads; and the transitive OCI closure of every retained manifest. Durable digest roots are repository-qualified, so moving a tag away from a deployed image keeps that manifest's config, layers, child manifests, and subject content protected. An unreadable, unsupported, or unresolvable manifest makes the blobs whose safety depended on it unknown instead of eligible.

Registry content is repository-scoped: a blob is served only to a repository that completed an upload of that digest, and a manifest may only reference config, layers, and child manifests that repository owns. Image labels never authorize deletion of a runtime image; eligibility requires a matching released ownership claim from the app that pinned it, no container use, and a complete inventory.

An exclusive GC lease serializes prune against app apply, deploy, start, restart, remove, recovery, and restore. Those paths hold a shared lease from validation/resource acquisition through the durable publication of that resource's protection, and an apply verifies the expected desired revision so a stale apply cannot overwrite a newer one.

source
1# Images Configuration
2
3Configure automatic image cleanup for Docker runtime images and local registry storage.
4
5## Overview
6
7When enabled, Gordon runs a scheduled image prune job that:
8
9- Prunes dangling runtime images that carry positive Gordon provenance.
10- Applies tag retention to registry repositories.
11- Preserves the `latest` tag.
12- Removes unreferenced blobs after tag cleanup.
13- Reports every protected and unknown candidate with its reason.
14
15The CLI `gordon images prune` runs the same use case and planner as the scheduled job, with the same defaults (keep `latest` + 3 previous tags, both scopes enabled: dangling runtime images and registry tag retention). App state existing on the server is normal and never disables pruning; the plan decides per candidate.
16
17## Configuration
18
19```toml
20[images]
21# Docker Hub, ghcr.io, quay.io, and Gordon's registry are already allowed.
22allowed_registries = ["registry.internal:5000"]
23require_digest = false
24
25[images.prune]
26enabled = false
27schedule = "daily"
28keep_last = 3
29```
30
31## Settings
32
33| Key | Type | Default | Description |
34|-----|------|---------|-------------|
35| `images.allowed_registries` | array | `[]` | Additional registry hostname+port entries. Docker Hub (`docker.io`, canonical pull host `registry-1.docker.io`), `ghcr.io`, `quay.io`, and Gordon's configured registry are always allowed. Include non-default ports, e.g. `"registry.example.com:5000"`. Hostnames are case-insensitive; one trailing dot and port `443` are canonicalized. This setting does not configure registry credentials. |
36| `images.require_digest` | bool | `false` | Require every image reference, including Gordon registry images, to use a valid `@sha256:<64 hex chars>` digest. |
37| `images.prune.enabled` | bool | `false` | Enables scheduled image cleanup |
38| `images.prune.schedule` | string | `"daily"` | Schedule preset: `hourly`, `daily`, `weekly`, `monthly` |
39| `images.prune.keep_last` | int | `3` | Number of newest non-`latest` tags kept per repository during registry cleanup (`latest` is always kept when present) |
40
41The policy validates registry names and strict SHA-256 digest syntax at manifest apply, digest resolution, deployment preflight, and immediately before each pull. It rejects malformed, userinfo-bearing, and ambiguous authorities. External app images currently require digest-pinned references, independently of `require_digest`; external tag resolution is not available. External pulls are anonymous, so adding a host to `allowed_registries` does not enable authenticated private-registry access. This hostname allowlist does **not** prove that DNS resolves to a public address and does not constrain runtime egress; enforce destination-level restrictions in the host firewall or runtime network policy.
42
43## Retention Behavior
44
45- `latest` is always preserved.
46- `keep_last` applies per repository and counts non-`latest` tags.
47- `keep_last` sets a minimum retained set, not a maximum: a tag named by durable app state, or referenced by the OCI closure of a protected manifest, survives beyond the window.
48- `keep_last = 0` skips registry tag/blob cleanup entirely (runtime dangling prune still runs).
49- Negative `keep_last` values are invalid.
50
51## Prune Safety
52
53A prune deletes only resources it can positively prove safe. Each candidate gets one verdict: `eligible` (deleted), `protected` (a durable fact claims it), or `unknown` (a required fact could not be read). Protected and unknown candidates are reported and left in place; an operation that deletes nothing is a success.
54
55Durable protection covers the DESIRED and ACTIVE state of every app, including stopped and partially converged services; recovery inhibitions; staged and committed apply intents; unfinished operation journal entries; runtime container use for running and stopped containers; recent uploads; and the transitive OCI closure of every retained manifest. Durable digest roots are repository-qualified, so moving a tag away from a deployed image keeps that manifest's config, layers, child manifests, and subject content protected. An unreadable, unsupported, or unresolvable manifest makes the blobs whose safety depended on it `unknown` instead of eligible.
56
57Registry content is repository-scoped: a blob is served only to a repository that completed an upload of that digest, and a manifest may only reference config, layers, and child manifests that repository owns. Image labels never authorize deletion of a runtime image; eligibility requires a matching released ownership claim from the app that pinned it, no container use, and a complete inventory.
58
59An exclusive GC lease serializes prune against app apply, deploy, start, restart, remove, recovery, and restore. Those paths hold a shared lease from validation/resource acquisition through the durable publication of that resource's protection, and an apply verifies the expected desired revision so a stale apply cannot overwrite a newer one.
60
61## Related
62
63- [CLI Images Command](../cli/images.md)
64- [Configuration Reference](./reference.md)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close