Step 1 of 5 · Reading · ~3 min
Read
Filesystems & Images
Registry Pull Protocol
docker pull alpine:3.19 looks like one download. It is a conversation, defined by the OCI Distribution Specification, and every property people find surprising about pulls — why the second one is instant, why two images share a download, why a failed pull leaves nothing behind — is visible in the transcript.
The conversation
GET /v2/library/alpine/manifests/3.19
-> 200, a manifest naming config + layer digests
for each layer digest, in order:
already in the local content store? -> skip it, no request at all
otherwise: GET /v2/library/alpine/blobs/sha256:<digest>
-> 200, stream the bytes to disk, then verify the hash
all layers present -> the image is ready
Three things are worth reading off that. The manifest is always refetched, because a tag is a mutable pointer and the client cannot know it still means what it meant yesterday. The blobs are not, because a digest is an identity — once sha256:abc… is in the store it is that content forever, and there is no cache-invalidation question to ask. And blobs are stored per digest, not per image, which is why pulling python:3.11-slim after debian:bookworm downloads only the difference.
Tags move, digests do not
alpine:latest today and alpine:latest in a year are two different manifests. The old blobs are still in your store under their own digests until garbage collection removes them; nothing was overwritten, because nothing addressed by content ever can be. This is exactly why deployments that must be reproducible pin image@sha256:… rather than a tag — a digest pin cannot drift, and a tag pin silently can.
When it fails
- 404 on the manifest — a typo in the repository, or a tag that does not exist. Nothing was transferred; the pull ends there.
- 404 on a blob — registry corruption or an over-eager garbage collection. The pull stops at that layer; the layers already fetched stay in the store (they are valid content, just not a complete image), and the image does not appear.
- 401 with
WWW-Authenticate: Bearer realm=…— the normal opening move for a private repository. The client fetches a token from the named realm and retries. Your exercise skips auth.
Why the exercise is a simulator
There is no privilege problem here and no network in the grader — the exercise is the protocol with the sockets removed, which leaves the part that carries the behaviour: order of requests, and what the cache lets you skip.
Your exercise: Simulate docker pull
| The registry / client does | Your simulator does |
|---|---|
| a blob exists in the registry at some size | PUT-BLOB <digest> <size> |
| a tag resolves to an ordered list of layer digests | PUT-MANIFEST <repo> <tag> <digests...> |
| the whole pull conversation, printed line by line | PULL <repo> <tag> |
| the local content store is emptied | FLUSH-CACHE |
The output is the answer, so the sequence has to be exact: the manifest request, then the manifest result, then per layer either CACHED <digest> with no request line, or the request line followed by 200 <size> bytes. A missing blob prints 404 blob, stops the loop immediately — later layers are never requested — and ends the pull with PULL FAILED. A layer only enters the cache after it was successfully fetched, which is what makes the second PULL of the same image all-cached and a FLUSH-CACHE between them fetch everything again.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…