Skip to content

Images pulled by digest are not detected (no RepoTags) #101

Description

@alex-woodhouse

Problem

When a container's image was pulled by digest (e.g. via image@sha256:... in a compose file), Docker does not assign any RepoTags to the image. This means container.image.tags returns an empty list, and get_container_names() yields no names to match against BACKUP_PROVIDERS.

This affects Immich in particular, whose compose file pins the postgres image by digest:

ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a

Result: RepoTags is [], so the immich-app/postgres pattern never matches, and the database is silently skipped.

Reproduction

  1. Run Immich (or any compose stack that pins images by digest)
  2. Run db-auto-backup
  3. Observe the Immich postgres container is not backed up despite immich-app/postgres being in the patterns list
$ docker inspect immich_postgres --format '{{.Image}}' | xargs docker image inspect --format '{{json .RepoTags}}'
[]

Suggested fix

Fall back to parsing container.attrs['Config']['Image'] when container.image.tags is empty. This field contains the original image reference from the compose file (e.g. ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:...), which can be parsed the same way as a tag.

Something like:

def get_container_names(container: Container) -> Iterable[str]:
    names = set()
    tags = container.image.tags

    # Fallback: if image was pulled by digest, there are no tags.
    # Use the Config.Image field instead, which has the original reference.
    if not tags:
        config_image = container.attrs.get("Config", {}).get("Image", "")
        if config_image:
            # Strip digest
            config_image = config_image.split("@")[0]
            tags = [config_image]

    for tag in tags:
        registry, image = docker.auth.resolve_repository_name(tag)
        if registry == docker.auth.INDEX_NAME:
            image = image.removeprefix("library/")
        image, _ = image.split(":", 1)
        names.add(image)
    return names

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions