Files
computer-playbook/docs/contributing/artefact/image/origin.md
T
Environment TestandClaude Sonnet 4.6 eebb3b9788 refactor(utils): move utils/docker/image_*.py into utils/docker/image/ subpackage
- Move image_discovery.py → utils/docker/image/discovery.py
- Move image_ref.py → utils/docker/image/ref.py
- Add utils/docker/image/__init__.py
- Update all imports and references across cli/, tests/, utils/, and docs/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 19:00:02 +02:00

4.3 KiB

Role Image Configuration 🐳

This document is the SPOT for how Docker images are declared, read, overridden, discovered, and mirrored. For the mirroring pipeline, see mirror.md.

Declaration Formats 📋

A role MAY declare images in two ways depending on how the image is used.

Compose Service Format — config/main.yml

Used for images that are started as part of the role's Docker Compose stack:

# roles/<role>/config/main.yml
compose:
  services:
    <service-name>:
      image: <image-name>   # e.g. nextcloud, quay.io/keycloak/keycloak
      version: <tag>        # e.g. latest, 31.0.0
      port: 8080
      # … other compose fields

Flat Image Format — defaults/main.yml

Used for images referenced directly in Ansible tasks or templates rather than through Compose (e.g. images pulled and run ad-hoc by a task). The role name is implicit from the file path roles/<role>/defaults/main.yml.

# roles/<role>/defaults/main.yml
images:
  <service-name>:
    image: <image-name>   # e.g. mcr.microsoft.com/playwright, postgres
    version: <tag>        # e.g. v1.58.2-noble
  • image MUST be the image name as it appears in a docker pull command, without the tag.
  • version MUST be the tag.
  • Images without an explicit registry prefix are treated as Docker Hub images.
  • Role-local image defaults MUST stay in images. Inventory-side overrides MUST NOT be written there.

Why defaults/ and not vars/? 🤔

The images: block holds role-local defaults, not inventory state. Keeping it in defaults/main.yml makes that intent explicit and keeps the role-side declaration separate from inventory-side images_overrides. Using vars/ would give those defaults higher precedence and blur the separation.

Read 📖

Roles MUST use lookup('image', ...) instead of direct images[...] access. The lookup MAY infer the current role id from role_name. If role_name is not available, the role id MUST be passed explicitly.

Supported forms:

{{ lookup('image', 'playwright', 'image') }}
{{ lookup('image', 'test-e2e-playwright', 'playwright', 'image') }}
{{ lookup('image', 'sys-ctl-hlth-csp', 'csp-checker', 'ref') }}

Supported want values:

Value Returns
all (default) the merged mapping
image the repository/image name
version the tag
ref image:version

Override ✏️

Inventory-side mirror and manual overrides MUST be written to images_overrides.<role>.<service>.{image,version}. lookup('image', ...) MUST prefer images_overrides and fall back field-wise to role-local images. Inventory generation MUST keep images_overrides separate from images so role defaults remain readable and stable.

Example host-vars override:

images_overrides:
  test-e2e-playwright:
    playwright:
      image: ghcr.io/example/mirror/mcr.microsoft.com/playwright
      version: v1.58.2-noble

Supported Registries 🌐

The following registries are discovered and mirrored:

Registry Example image
docker.io (Docker Hub) postgres, nextcloud, prom/prometheus
quay.io quay.io/keycloak/keycloak
ghcr.io ghcr.io/mastodon/mastodon
mcr.microsoft.com mcr.microsoft.com/playwright

Images from other registries are ignored by the discovery and mirroring tooling.

Discovery 🔍

utils/docker/image/discovery.py scans all roles and yields ImageRef objects:

Source file Key path Used for
config/main.yml compose.services.<svc>.image + .version Compose-managed services
defaults/main.yml images.<svc>.image + .version Ad-hoc task images

An ImageRef carries: role, service, name (without registry), version, source (full pull ref), registry, and source_file.

Mirror Integration 🪞

After discovery, images are mirrored to GHCR and the mirror URLs are injected back into host variables by the inventory creator.

  • config/main.yml declarations are resolved back through mirrors.yml.applications.<role>.compose.services.<service>
  • defaults/main.yml declarations are resolved back through mirrors.yml.images.<role>.<service> and land in host vars under images_overrides.<role>.<service>

See mirror.md for the full flow.