Skip to content

Docker

NORA implements the OCI Distribution Spec 1.1 for storing and proxying Docker container images. Push your own images and optionally cache pulls from Docker Hub or other upstream registries.

Tag and push images using NORA’s address directly:

Terminal window
# Tag an existing image
docker tag myapp:latest nora.example.com:4000/myapp:latest
# Push
docker push nora.example.com:4000/myapp:latest
# Pull
docker pull nora.example.com:4000/myapp:latest

For insecure (HTTP) registries, add NORA to your Docker daemon config (/etc/docker/daemon.json):

{
"insecure-registries": ["nora.example.com:4000"]
}

Restart Docker after editing.

NORA can act as a pull-through cache for Docker Hub and other registries. When an image is not found locally, NORA fetches it from the configured upstream(s).

config.toml:

[docker]
enabled = true
proxy_timeout = 300
[[docker.upstreams]]
url = "https://registry-1.docker.io"
[[docker.upstreams]]
url = "https://ghcr.io"
auth = "user:token" # optional basic auth

Environment variables:

VariableDescriptionDefault
NORA_DOCKER_ENABLEDEnable Docker registrytrue
NORA_DOCKER_PROXIESComma-separated upstream URLs (url1,url2 or url1|auth1,url2)https://registry-1.docker.io
NORA_DOCKER_PROXY_TIMEOUTUpstream timeout in seconds300

From 1.4.0 a pushed tag is answered by the pushed manifest only, also when the tag was pulled through the proxy before the push: the push records itself in the tag’s metadata and removes the proxied copy. Curation dates such a tag by the push. A manifest requested by digest always has that digest — an upstream answer with another digest counts as a failed upstream, and a push by digest with other bytes is 400 DIGEST_INVALID. Tags ending in .meta or .stats are refused with 400.

Endpoint / FeatureStatusNotes
/v2/ (version check)FullAPI version check
/v2/_catalog (list repos)FullList all repositories
/v2/{name}/tags/listFullList image tags
/v2/{name}/manifests/{ref} GET/HEAD/PUT/DELETEFullBy tag or digest
/v2/{name}/blobs/{digest} GET/HEAD/DELETEFullDownload/check/delete layers
/v2/{name}/blobs/uploads/ POST/PATCH/PUTFullChunked upload flow
Repository paths of any depthFullorg/image, org/team/project/image
Bearer token authFullWWW-Authenticate challenge
Helm OCI (helm push / helm pull)FullVia standard /v2/ API
Cross-repo blob mountFullPOST /v2/{name}/blobs/uploads/?mount=<digest>&from=<repo>
Referrers API—OCI 1.1 referrers not implemented
  • Large monolithic blob PUT (>~500 MB) may fail even with high body limit.
  • Blobs are stored per repository: a layer that several repositories hold is stored in each of them (a cross-repository mount hard-links it on local storage and copies it on object storage); there is no global blob store. GC reclaims a repository’s unreferenced layers.
  • Helm repo index (index.yaml) is not supported; use Helm OCI workflow only.