Containers & Orchestration·7 min read·

Reading an OCI Image: Layers, Manifests, and What They Mean

Open up a container image to see its layers, manifest digest, and config — and learn why filesystem layers are content-addressed.

NB

Netbay Infrastructure Team

Netbay Engineering

On this page

A container image is not one big file — it is a tree of metadata and filesystem layers that a pull resolves into a runnable root. If you have ever wondered why docker images shows a size that never matches the installed footprint, or why two images share storage, the answer lives in the layer model. Reading an image by hand is the fastest way to make it all concrete.

This post walks what an OCI image actually contains and how to inspect it without squinting at binary data.

The Three Pieces of an Image

An OCI image is a manifest plus a config plus a set of blobs. The manifest lists the config and the layers; the config holds JSON about environment, entrypoint, and user; each blob is a tarball of a filesystem layer, identified by a sha256 digest. The entire thing is content-addressed: layers are stored once no matter how many images reference them.

manifest -> config digest -> layer[0..n] digests config.json env, cmd, user layer blobs dir /usr/bin dir /etc file /app/server filesystem root

Inspecting With Standard Tools

Skip the registry web UI and read the image locally. Docker and Podman expose the manifest and config as JSON you can pipe through jq:

bash
docker image inspect nginx:alpine | jq '.[0].Config'
docker manifest inspect nginx:alpine > manifest.json
jq -r '.config.digest, .layers[].digest' manifest.json

The layer digests are the same sha256 identifiers the registry stores. Each digest names a blob, and those blobs are what gets pulled when a host runs the image.

Unpacking a Layer by Hand

A layer is a gzipped tar of filesystem changes. You can pull a blob from a registry and list or extract it directly to see the classic tarball structure and the whiteout files that record deletions. This is precisely what docker export flattens for you.

bash
# squash the whole image into one filesystem and explore it
docker export $(docker create nginx:alpine) -o rootfs.tar
tar -tvf rootfs.tar | head -20
mkdir /tmp/roots && tar -xf rootfs.tar -C /tmp/roots
find /tmp/roots/etc/nginx -maxdepth 1 | head

Why Content Addressing Matters

Because every layer is referenced by its hash, the registry and client can trust that a blob has not changed in transit — the digest is checked on pull. It also means deduplication: the ubuntu base layers you share across several images are downloaded once. And it enables immutable tags: by default a digest points to a fixed set of blobs, which is why digest-pinning deploys are reproducible.

bash
# run a specific image version by digest, never by mutable :latest
docker pull nginx@sha256:4a2f5d9a...   && docker run -d nginx@sha256:4a2f5d9a...

Takeaway

Images are content-addressed manifests plus filesystem layers, and reading them with inspect and export demystifies size, caching, and sharing. That knowledge also helps you diagnose ballooning images — more on that in the image-bloat post. Start a fresh Netbay instance at netbayhosts.in, pull one of your images, and poke at its layers with the commands above.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance