Skip to content

feat(container): support containerd ns query parameter for multi-registry mirroring - #417

Closed
yunaremaia wants to merge 1 commit into
git-pkgs:mainfrom
yunaremaia:main
Closed

yunaremaia wants to merge 1 commit into
git-pkgs:mainfrom
yunaremaia:main

Conversation

@yunaremaia

Copy link
Copy Markdown

Summary

The container handler currently routes /v2/ requests by path only: unprefixed names go to the default registry, and named upstreams require the reserved upstream/{name}/ path prefix. containerd's hosts.toml mirror mechanism instead appends ?ns=<registry-host> to every request, which the handler ignores today. Multi-registry mirroring therefore needs one hosts.toml per registry with override_path = true pointing at the matching prefix; wildcard setups (certs.d/_default, k3s mirrors: "*") cannot work at all, because the mirror never learns which registry a request is for.

This PR adds ns support so a single host entry can mirror every configured registry.

Changes

  • Closed-world lookup key, never a dial target: ns resolves Docker Hub aliases (docker.io, index.docker.io, registry-1.docker.io) and the host of the configured oci_default to the default route, and hosts derived from the existing upstream.oci URLs to their named upstream. No new config keys.
  • Unknown ns returns OCI-error 404 (NAME_UNKNOWN) so containerd falls back to its next host / server entry. Requests without ns behave exactly as today.
  • Path is the verbatim upstream repository when ns is present; the reserved upstream/ prefix is rejected on that route so ns-routed requests cannot mint cache keys belonging to the prefix route.
  • ns is stripped before forwarding the tags-list query upstream (it previously leaked verbatim).
  • Cache identities are reused so an image pulled via ns, via upstream/{name}/, or unprefixed shares cache entries; manifest keys already include the resolved registry URL.
  • Host matching is case-insensitive and normalizes scheme-default ports.
  • The ns host index is built after NewContainerHandlerWithRegistry overrides the default registry URL, otherwise a custom oci_default host would 404.
  • Duplicate hosts across upstream.oci entries (or colliding with the default route) get a startup warning and a deterministic winner rather than a validation error.

Test coverage

6 new tests covering:

  • ns routing to default and named registries
  • Unknown ns → 404 with no upstream contact
  • ns stripped from upstream query
  • Case-insensitive host matching
  • Docker Hub aliases
  • Custom oci_default host
  • Cache sharing across routes

Full handler suite passes (11.6s, 0 failures).

Closes #303

…stry mirroring

The container handler currently routes /v2/ requests by path only: unprefixed
names go to the default registry, and named upstreams require the reserved
upstream/{name}/ path prefix. containerd's hosts.toml mirror mechanism instead
appends ?ns=<registry-host> to every request, which the handler ignores today.

Add ns support so a single host entry can mirror every configured registry:

- Treat ns as a closed-world lookup key, never a dial target. Docker Hub
  aliases (docker.io, index.docker.io, registry-1.docker.io) and the host of
  the configured oci_default resolve to the default route; hosts derived from
  the existing upstream.oci URLs resolve to their named upstream.
- Unknown ns returns an OCI-error 404 (NAME_UNKNOWN) so containerd falls back
  to its next host / server entry. Requests without ns behave exactly as today.
- When ns is present, the path is the verbatim upstream repository; the
  reserved upstream/ prefix is rejected on that route.
- ns is stripped before forwarding the tags-list query upstream.
- Cache identities are reused so an image pulled via ns, via upstream/{name}/,
  or unprefixed shares cache entries.
- Host matching is case-insensitive and normalizes scheme-default ports.

The ns host index is built after NewContainerHandlerWithRegistry overrides the
default registry URL, otherwise a custom oci_default host would 404.

Closes git-pkgs#303
@yunaremaia

Copy link
Copy Markdown
Author

Closing as a duplicate of #405, which implements the same ns support and has been open since 2026-10-03 with active maintainer review.

Comparing the two: #405 covers the same closed-world lookup, Docker Hub aliases, case-insensitive host matching, upstream/ rejection and shared cache entries, and goes further on three points I had not handled — registry URLs with a path are deliberately not indexed (avoids silently routing art.corp/docker-local/app into a different repository), the tag-list Link header is rewritten per request with a cache-key format marker so an older binary never sees the raw link, and README plus docs/configuration.md get a containerd section with a _default hosts.toml example. It also has real test coverage in container_ns_test.go including IPv6 and port normalization.

Nothing in this PR is unique enough to keep open. Thanks for the review exchange on #405 — the override_path point about per-registry mirrors is a good catch.

@yunaremaia yunaremaia closed this Oct 9, 2026
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.

Support the containerd ns query parameter for multi-registry mirroring

1 participant