Skip to content

Docker repos: adopt the OCI standard label paradigm (org.opencontainers.image.*) #363

Description

@ptr727

Proposal

Standardize Docker image metadata across the fleet on the OCI pre-defined annotation keys (org.opencontainers.image.*), replacing the ad-hoc label sets the Docker repos carry today. Surfaced while auditing labels at ptr727/ESPHome-NonRoot; nothing about it is repo-specific.

Why now

The labels in these repos were written when org.label-schema.* was the live convention and OCI annotations were still a proposal. Both facts have since changed:

  • label-schema.org deprecated itself in favor of the OCI keys.
  • OCI annotations were standardized in image-spec v1.0.0 (July 2017) and extended in v1.1.0 (2024). They have been a real standard for roughly eight years.

So the ad-hoc naming is not a deliberate choice we are maintaining - it is a snapshot of what was available when it was written, and it has quietly fallen behind.

What it costs today

Ad-hoc keys are invisible to every consumer that reads image metadata: registry UIs, Docker Hub, GitHub Packages, Docker Scout, Renovate, and provenance/SBOM tooling all key off org.opencontainers.image.*. An image can be fully and correctly labelled and still present as having no metadata at all.

That is not hypothetical. While inspecting a freshly published ESPHome-NonRoot image I read org.opencontainers.image.version, got an empty result, and briefly took it for a build defect. The image was correctly labelled the whole time - under version, which nothing but a human reading docker inspect will look for. If it misleads someone who wrote the label block that day, it will mislead tooling every time.

Current state (ESPHome-NonRoot as the concrete case)

name                   = ESPHome
version                = 1.9.10       # NBGV SemVer2 release tag
esphome_version        = 2026.7.0     # upstream pin
device_builder_version = 1.6.4        # upstream pin
description            = ...
maintainer             = ...

Three of these have direct OCI equivalents. maintainer is additionally a deprecated Docker legacy label, superseded by org.opencontainers.image.authors.

Proposed paradigm

1. OCI keys carry the standard metadata. At minimum .title, .description, .version, .revision, .source, .url, .licenses, .authors, .created; optionally .documentation and .base.name / .base.digest.

2. .version is the repo's own release version, not an upstream one. For the NBGV repos that is the SemVer2 tag already passed as LABEL_VERSION. This matters because a repo can ship several upstream components at different versions - ESPHome-NonRoot pins both esphome and esphome-device-builder - so no single upstream version can stand in for the image's identity. Concatenating them into a synthetic version is not an option: it breaks SemVer2, which NBGV's nugetPackageVersion.semVer: 2 contract and the release tagging depend on.

3. Upstream pins stay as explicit non-OCI labels. OCI has no key for "the version of the third-party thing inside." Keep esphome_version / device_builder_version as they are; they are legitimately repo-specific and the standard does not cover them. This is additive - no consumer of the existing labels breaks.

4. Prefer explicit LABEL + build-args over docker/metadata-action. The action derives source/revision/created automatically, but it also wants to own tag computation, which conflicts with NBGV-driven versioning in these repos. Using it for labels alone means a new SHA-pinned action earning its keep on half its job. An explicit block matches how the Dockerfiles already work.

Implementation trap worth encoding in the rule

org.opencontainers.image.created and .revision change on every build. An ARG whose value changes invalidates that layer and everything below it, so placing these early in the final stage silently destroys the Docker layer cache the pipelines depend on. Pin the volatile args as late in the stage as possible, and verify against a cached rebuild rather than assuming - the failure mode here is a slow pipeline, not a red one, so nothing reports it.

Scope

Applies to every fleet repo that publishes a Docker image. Suggest landing the label block plus the cache-ordering note as a template convention, then carrying it downstream per repo.

I have not changed anything in ESPHome-NonRoot; raising here first so the paradigm is decided once rather than per repo.

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