skip to content

How do you attach metadata such as source repository, version and build revision to a container image, and which key names are standardized?

level: middleimportance: should knowfreq 38%

answer

  1. config metadata, not a filesystem layer
  2. org.opencontainers.image.{source,revision,version,created}
  3. ARG + LABEL for commit SHA; put labels last for cache
  4. inherited from base, overwrite by repeating key
  5. never secrets; MAINTAINER is dead

basics

~20 s

Use LABEL key=value in the Dockerfile; values land in the image config and are readable with docker inspect. Use the standard OCI keys org.opencontainers.image.* (source, revision, version, created, title, licenses) so tooling and registries recognize them. MAINTAINER is obsolete.

solid answer

~50 s

`LABEL <key>=<value> ...` writes arbitrary key/value metadata into the image configuration. Read it with `docker inspect --format '{{json .Config.Labels}}'` and filter images with `docker image ls --filter label=team=payments`. Use the OCI predefined annotation keys rather than inventing names: org.opencontainers.image.source (repo URL), .revision (commit SHA), .version, .created (RFC 3339 timestamp), .title, .description, .licenses, .vendor, .base.name and .base.digest. Registries and supply-chain tooling read them - GitHub Container Registry, for example, links a package to its repository from .source. Anything custom should use your own reverse-DNS prefix. Dynamic values come from build args: `ARG GIT_SHA` then `LABEL org.opencontainers.image.revision=$GIT_SHA`. Put labels late in the Dockerfile so changing a commit SHA does not invalidate expensive earlier layers. Labels are inherited from the base image and overridden by repeating the key. They are plaintext, visible to anyone who can pull the image - never put secrets in them. MAINTAINER is deprecated in favor of org.opencontainers.image.authors.

code

dockerfile · 9 lines
dockerfile
ARG GIT_SHA=unknown
ARG BUILD_DATE=1970-01-01T00:00:00Z
LABEL org.opencontainers.image.title="payments-api" \
      org.opencontainers.image.source="https://example.com/org/payments-api" \
      org.opencontainers.image.revision="$GIT_SHA" \
      org.opencontainers.image.version="1.4.2" \
      org.opencontainers.image.created="$BUILD_DATE" \
      org.opencontainers.image.licenses="Apache-2.0" \
      com.example.team="payments"

go deeper

for a junior

Know that LABEL adds key/value metadata readable via docker inspect, and name a couple of standard keys.

for a middle

Use the OCI key set, wire dynamic values through ARG, and explain cache placement and inheritance from the base image.

for a senior

Explain traceability from a running container back to a commit, label-based filtering in operations, and label versus manifest annotation.

for a principal

Define the mandatory metadata contract for all org images and how supply-chain tooling, provenance and audits depend on it being uniform.

## The instruction `LABEL key=value key2="value with spaces"` stores metadata in the image's configuration object, not in any filesystem layer. Multiple pairs in one instruction are preferred to many instructions - each LABEL adds a history entry and a config change. Labels are inherited from the base image; repeating a key in a child image overwrites it, and setting a key to an empty value is how you blank an inherited one. ## Standardized keys The OCI image spec defines a set of predefined annotation keys, conventionally used as label keys too: - org.opencontainers.image.created - build timestamp, RFC 3339 - org.opencontainers.image.authors - org.opencontainers.image.url and .documentation - org.opencontainers.image.source - URL of the source repository - org.opencontainers.image.version - semantic version or tag - org.opencontainers.image.revision - exact commit the build came from - org.opencontainers.image.vendor, .licenses (SPDX expression), .title, .description - org.opencontainers.image.base.name and .base.digest - what this image was built FROM Using these is what makes the metadata useful to something other than your own scripts: registry UIs surface them, scanners and SBOM tooling correlate on them, and .revision is what turns a running image back into an auditable commit. Custom keys should be reverse-DNS namespaced (com.example.team) so they cannot collide with anyone else's. ## Dynamic values Labels are static text, so dynamic values arrive through build arguments: declare `ARG GIT_SHA` / `ARG BUILD_DATE` and reference them. Because every build passes a different SHA, put the LABEL block at the very end of the Dockerfile; otherwise the changed instruction invalidates the build cache for everything below it and you rebuild dependencies on every commit. ## Labels versus annotations A label lives in the image *config* blob, which a client must pull to read. OCI *annotations* live on the manifest or index - the descriptor level - and can be read without fetching the config, which is why registries and multi-platform indexes use them. Buildx can set them (`--annotation`), and tooling can add them to an existing index without rebuilding layers. In interviews it is enough to know the distinction: LABEL is image-level metadata baked at build time, annotations are manifest-level metadata that can describe a specific platform variant or a whole index. ## Consuming labels `docker inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' img` extracts one value. `docker image ls --filter label=com.example.team=payments` filters locally, and `docker ps --filter label=...` filters containers, since a container inherits its image's labels and can add more with `docker run --label`. In practice the highest-value habit is: every image carries .source and .revision, so any running container can be traced to a commit. ## Anti-patterns Secrets in labels: they are plaintext in the config, distributed with the image and readable by anyone who can pull it. Long free-text blobs: the config is fetched on every pull. Inventing keys that duplicate the OCI ones with different spellings, which defeats tooling. And MAINTAINER, which is a deprecated instruction that predates labels; use org.opencontainers.image.authors instead.

  • Why should the LABEL block go at the end of the Dockerfile?
    Label values such as the commit SHA change on every build, and a changed instruction invalidates the build cache for that step and everything after it. Placing labels last means only a cheap metadata step is rebuilt, while dependency installation and compilation stay cached.
  • What is the difference between a LABEL and an OCI annotation?
    A LABEL is stored in the image configuration blob, so a client must pull the config to read it and it belongs to one built image. An annotation is attached to the manifest or the multi-platform index descriptor, readable without pulling the config, and can be added to an index by tooling without rebuilding layers. They serve similar documentation purposes at different levels of the image structure.

saying these in an interview costs you the question

  • Believing each LABEL adds a large filesystem layer
  • Storing tokens, credentials or internal URLs in labels because 'only we pull the image'
  • Still using MAINTAINER instead of org.opencontainers.image.authors
  • Inventing ad-hoc key names when a standard OCI key exists
  • Putting a commit-SHA label early in the Dockerfile and destroying the build cache

context