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.
Client Configuration
Section titled “Client Configuration”Tag and push images using NORA’s address directly:
# Tag an existing imagedocker tag myapp:latest nora.example.com:4000/myapp:latest
# Pushdocker push nora.example.com:4000/myapp:latest
# Pulldocker pull nora.example.com:4000/myapp:latestFor insecure (HTTP) registries, add NORA to your Docker daemon config (/etc/docker/daemon.json):
{ "insecure-registries": ["nora.example.com:4000"]}Restart Docker after editing.
Upstream Proxy
Section titled “Upstream Proxy”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 = trueproxy_timeout = 300
[[docker.upstreams]]url = "https://registry-1.docker.io"
[[docker.upstreams]]url = "https://ghcr.io"auth = "user:token" # optional basic authEnvironment variables:
| Variable | Description | Default |
|---|---|---|
NORA_DOCKER_ENABLED | Enable Docker registry | true |
NORA_DOCKER_PROXIES | Comma-separated upstream URLs (url1,url2 or url1|auth1,url2) | https://registry-1.docker.io |
NORA_DOCKER_PROXY_TIMEOUT | Upstream timeout in seconds | 300 |
Pushed Tags and the Upstream
Section titled “Pushed Tags and the Upstream”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.
Features
Section titled “Features”| Endpoint / Feature | Status | Notes |
|---|---|---|
/v2/ (version check) | Full | API version check |
/v2/_catalog (list repos) | Full | List all repositories |
/v2/{name}/tags/list | Full | List image tags |
/v2/{name}/manifests/{ref} GET/HEAD/PUT/DELETE | Full | By tag or digest |
/v2/{name}/blobs/{digest} GET/HEAD/DELETE | Full | Download/check/delete layers |
/v2/{name}/blobs/uploads/ POST/PATCH/PUT | Full | Chunked upload flow |
| Repository paths of any depth | Full | org/image, org/team/project/image |
| Bearer token auth | Full | WWW-Authenticate challenge |
Helm OCI (helm push / helm pull) | Full | Via standard /v2/ API |
| Cross-repo blob mount | Full | POST /v2/{name}/blobs/uploads/?mount=<digest>&from=<repo> |
| Referrers API | — | OCI 1.1 referrers not implemented |
Known Limitations
Section titled “Known Limitations”- 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.