Images, the OCI Specification and Registries

Topics Covered

What an Image Specification Actually Defines

Descriptors are the only way objects reference each other

The manifest, and what it contains

The configuration object holds the runtime behaviour

The index sits above the manifests

Media types, and why they cause failures

Reading all of this from a registry directly

Annotations, and the metadata nobody fills in

Artefacts that are not images

Empty blobs and the small conventions

An image is a small graph of content-addressed objects described by two specifications, one for the image format and one for the registry protocol. Reading them once is worth more than any amount of tooling familiarity, because every confusing registry behaviour is a consequence of something written there.

Descriptors are the only way objects reference each other

Every reference in the format is a descriptor, which is a three-field object, and once that clicks the whole graph is obvious.

A descriptor carries a media type naming what the referenced object is, a digest naming its exact bytes, and a size in bytes. Optional fields add platform information, annotations and alternative download locations. Nothing references anything by name, position or tag; every edge in the graph is a digest.

That single rule gives the format its properties. Verification is possible at every step because the expected hash is known before fetching. Deduplication is automatic because identical content has one identifier. And immutability is structural: changing any object changes its digest, which changes every descriptor pointing at it, all the way up.

The manifest, and what it contains

A manifest is the per-platform image, and it is a small document, usually under a kilobyte.

It holds a descriptor for the configuration object, an ordered list of descriptors for the layers, an optional subject descriptor used for attaching related artefacts, and annotations. Its own digest is the image's digest, which is why a digest reference identifies exactly one set of bytes forever.

What a manifest does not contain is any of the actual content, so fetching it is cheap, and that is deliberate: a client can read the layer sizes, the platform and the configuration digest before transferring anything. Tools that report an image's size without pulling it work by summing the layer sizes in the manifest.

The configuration object holds the runtime behaviour

The configuration is where everything a runtime needs to start the container lives, and it is a plain document.

It names the entrypoint and default command, the environment variables, the working directory, the user, the exposed ports, the declared volumes, the stop signal and the architecture and operating system. It also holds the ordered list of uncompressed layer identifiers, which is what lets a runtime verify it unpacked the right contents, and a history array with one entry per build instruction.

The history is the underused part. Each entry records the command that produced it and whether it created a layer, so an image's entire build can be read from the registry without pulling a byte. That is how you answer what is in an image you do not trust enough to run.

Every edge in the graph is a descriptor carrying a media type, a digest and a size, which is what makes verification, deduplication and immutability structural rather than optional.

The index sits above the manifests

One more level exists above the manifest and it is what makes a single reference work on several architectures.

An index is a list of manifest descriptors, each annotated with the platform it is for: architecture, operating system, and optionally variant, features and operating system version. A client resolving a reference fetches the index, selects the entry matching its own platform, and then fetches that manifest.

So a reference can point at an index or directly at a manifest, and the client has to handle both. The index's digest is what a multi-architecture reference by digest names, which means pinning a multi-architecture image by digest still works correctly on every platform, a property people often assume they have to give up.

Media types, and why they cause failures

The media type strings are the most boring part of the specification and the source of a specific class of frustrating error.

There are two parallel sets, one from the older ecosystem-specific format and one from the vendor-neutral specification, with near-identical semantics and different strings. Registries and clients vary in which they accept, convert between or reject, and some registries reject types they do not recognise outright rather than storing them opaquely.

The failures look like a push succeeding and a pull failing, or an image working with one tool and not another, and the diagnosis is to fetch the manifest raw and read the types. Knowing that two vocabularies exist turns an inexplicable error into a five-minute fix, usually a flag selecting the output format.

Reading all of this from a registry directly

Every object is fetchable over plain HTTP with a token, which makes the whole graph inspectable with ordinary tools.

bash
1# resolve a tag to an index or manifest, then read the config
2curl -sH "Authorization: Bearer $TOKEN" \
3  -H 'Accept: application/vnd.oci.image.index.v1+json' \
4  https://registry.example.com/v2/team/app/manifests/1.4 | jq '.manifests[].platform'
5curl -sH "Authorization: Bearer $TOKEN" \
6  https://registry.example.com/v2/team/app/blobs/sha256:<config-digest> | jq '.history[-3:]'

Doing this a few times makes the format concrete, and it is also the fastest way to answer production questions: which platforms an image supports, what its entrypoint is, how large each layer is, and what the last build instruction did.

Annotations, and the metadata nobody fills in

Both manifests and indexes carry a free-form annotation map, and a short list of standard keys exists that almost nothing populates.

The conventional keys cover the source repository, the revision that produced the image, the build timestamp, the version, the licence, a description and a documentation link. A registry that displays them turns an image listing into something a human can navigate, and a tool that reads them can answer which commit produced this image without any external system.

Filling them in costs one build flag and pays off during incidents, when the question is which code is running and the deployment specification only has a digest. The reason to prefer annotations over an internal convention is that the standard keys are what other tools look for, so a scanner, a policy engine or a registry interface will use them without being taught.

Artefacts that are not images

The same format carries things that have nothing to do with containers, which is why the specification is worth knowing beyond images.

A manifest can declare an artefact type and reference arbitrary blobs, so package managers, policy bundles, machine learning models and configuration bundles are all distributed through registries now. The benefits are the ones the format already has: content addressing, deduplication, signatures, replication and an access control model that organisations already run.

For a platform team this matters because the registry stops being an image store and becomes general artefact infrastructure, which changes its capacity planning, its retention policy and its availability requirements. It also means a registry that rejects unfamiliar media types is a limitation rather than a quirk, because it cannot host those artefacts at all.

Empty blobs and the small conventions

A few small details in the specification cause confusion out of proportion to their size, and the empty blob is the main one.

An artefact that has no configuration still needs a configuration descriptor, so the specification defines a canonical empty object with a fixed digest that every registry is expected to hold. Tools that do not know about it try to upload it, which some registries reject as a zero-length blob, and the error is obscure.

The related convention is the artefact type field, which replaced an earlier practice of overloading the configuration's media type to indicate what an artefact was. Registries and tools support one, the other or both, which is why pushing a non-image artefact sometimes works with one tool and not another against the same registry. Reading the raw manifest is again the fastest way to see which convention is in play.

Level Expectations

A strong answer describes the graph rather than the tooling: an optional index of per-platform manifests, each manifest referencing a configuration object and ordered layer blobs, with every edge a descriptor carrying media type, digest and size. From that the candidate can derive why a digest reference is exact and a tag is not, why fetching a manifest is cheap enough to inspect before pulling, and why a multi-architecture image pinned by digest still resolves correctly per platform.