Skip to content

Use Docker Hub's server-side manifest copy when possible - #148

Open
tianon wants to merge 1 commit into
docker-library:mainfrom
infosiftr:docker-hub-manifest-copy
Open

tianon wants to merge 1 commit into
docker-library:mainfrom
infosiftr:docker-hub-manifest-copy

Conversation

@tianon

@tianon tianon commented Oct 31, 2026

Copy link
Copy Markdown
Member

Docker Hub has a POST /v2/<dst>/_docker/copy/image endpoint (basically a blob mount, but for a manifest) that copies a manifest, all of its child manifests, and all of their blobs server side. Try it before copying client side, both for the top level of CopyManifest and for each child manifest in EnsureManifest, and fall back to the existing client side copy on any failure. A 12 platform index with 15 layers each went from 243 requests (~12.4s) to 27 requests (~2s) against a local instance of Hub's internal registry implementation with 40ms of added latency, which also matters for the 300 requests per minute budget Hub gets in rate-limits.go.

The endpoint is feature flagged by namespace, so remember per host when it does not exist (a non-JSON 404 or a 405) and per source/target namespace pair when it is not enabled (a 400 MANIFEST_BLOB_UNKNOWN), to avoid wasting a request on every manifest. Check whether the target already has the manifest first (the endpoint redoes the whole push instead of no-oping), and send the request through the same client transport (authentication, rate limiting, User-Agent) as everything else.

Note that CopyManifest on the server side path only knows the digest of what it copied, so MediaType and Size on the returned descriptor are only filled in when a lookup was needed anyhow.

Add tests that run EnsureManifest and CopyManifest against a fake Docker Hub (an in-memory registry plus the copy endpoint and each of its failure modes).

Assisted-By: "claude my eyes right out"

Docker Hub has a `POST /v2/<dst>/_docker/copy/image` endpoint (basically a blob mount, but for a manifest) that copies a manifest, all of its child manifests, and all of their blobs server side.  Try it before copying client side, both for the top level of `CopyManifest` and for each child manifest in `EnsureManifest`, and fall back to the existing client side copy on any failure.  A 12 platform index with 15 layers each went from 243 requests (~12.4s) to 27 requests (~2s) against a local instance of Hub's internal registry implementation with 40ms of added latency, which also matters for the 300 requests per minute budget Hub gets in `rate-limits.go`.

The endpoint is feature flagged by namespace, so remember per host when it does not exist (a non-JSON 404 or a 405) and per source/target namespace pair when it is not enabled (a 400 `MANIFEST_BLOB_UNKNOWN`), to avoid wasting a request on every manifest.  Check whether the target already has the manifest first (the endpoint redoes the whole push instead of no-oping), and send the request through the same client transport (authentication, rate limiting, User-Agent) as everything else.

Note that `CopyManifest` on the server side path only knows the digest of what it copied, so `MediaType` and `Size` on the returned descriptor are only filled in when a lookup was needed anyhow.

Add tests that run `EnsureManifest` and `CopyManifest` against a fake Docker Hub (an in-memory registry plus the copy endpoint and each of its failure modes).

Assisted-By: "claude my eyes right out"
@tianon
tianon requested a review from a team as a code owner October 31, 2026 00:02
@tianon

tianon commented Oct 31, 2026

Copy link
Copy Markdown
Member Author

Gonna incorporate this into my personal deployment and see how it goes (tianon is in the feature flag 🤓).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants