Configuration reference

Configuration keys, defaults, authentication, and safe reloads.

The configuration file is YAML. Unknown keys are rejected, so a misspelt option fails at startup instead of being silently ignored. The file can be written by hand, or changed through the CLI, which validates every change before saving.

Durations use Go syntax: 500ms, 5s, 1m30s.

config.d drop-in configuration and merging

Use config.d/ for supplemental configuration files (drop-ins) instead of keeping every setting in the main configuration file. With config.yaml, the service also loads visible *.yaml and *.yml files directly inside the adjacent config.d/ directory. The directory name is the main configuration filename without its extension, followed by .d: --config /path/proxy.yml uses /path/proxy.d/. The main file is required; the drop-in directory is optional. Drop-in files load in filename lexicographic order after the main file, except local-overrides.yaml, which is reserved for existing-URL patches and loads last. Hidden/temp/backup files and subdirectories are ignored.

Mappings merge recursively; later explicitly declared values override earlier ones, including false. Defaults are applied only after merging. Upstreams merge by normalized URL within each registry: first occurrence determines position, later explicit options override the same URL. upstreams: [] does not clear previous sources. Scheme/host case, default ports, and a root slash are normalized; meaningful paths remain distinct. No upstream ID is required. Duplicate keys, multiple YAML documents, YAML aliases/merge keys, unknown fields, and unsupported explicit versions are rejected. An invalid configuration file rejects the entire update, keeping the running configuration unchanged.

local-overrides.yaml patches only already-provided URLs, so local disabled choices survive updates without reintroducing URLs removed from all sources. See remote catalogs for subscription files and backups.

Top level

KeyDefaultDescription
version1Schema version. Only 1 is accepted.
log_levelinfoOptional logging level: debug, info, warn, or error. Reloadable; an explicit serve --log-level overrides the file. Omitted from generated configuration by default.
listen127.0.0.1:24817Address the registry API listens on.
advertise(derived)host:port that dockerd uses to reach the proxy. Required in practice when listen is a wildcard address. Must name a concrete host.
admin.socketper-user defaultPath of the admin socket. The default is $XDG_RUNTIME_DIR/oci-mirror-proxy/admin.sock, or the user cache directory when XDG_RUNTIME_DIR is unset.
registriesdocker.ioMap from registry alias to its settings.

The address dockerd connects to is advertise when set. Otherwise it is listen, with wildcard hosts (0.0.0.0, ::) mapped to 127.0.0.1.

For debugging, add top-level log_level: debug and run oci-mirror-proxy reload. Debug logs correlate requests with request_id and record routing strategy, candidates, attempt latency/status, breaker skips, hedging and the selected upstream. upstream is the legacy label when present, otherwise its URL; via is its configured base URL. upstream hit means a successful response; upstream selected identifies the actual winner (other successful racing responses may be discarded). This does not indicate the mirror's internal cache status. Latency ends at response headers, not blob completion. Resumed attempts include range. Attempt debug logs do not include authentication headers or signed redirect URLs.

Registries

Each key under registries is a registry alias:

  • docker.io is served at the root path, /v2/, which is where dockerd looks.
  • Any other alias, such as ghcr.io, is served under a path prefix, /ghcr.io/v2/. The alias must be a host name without a port. v2 is reserved.
KeyDefaultDescription
originhttps://registry-1.docker.io for docker.io, otherwise https://<alias>Official endpoint. dockerd falls back to it when the proxy cannot serve a request.
race.enabledtrueEnable hedged requests for this registry.
race.delay500msWith racing on, start another upstream if nothing has answered within this time. Must be positive.
race.max_parallel3Maximum concurrent attempts per request. Must be at least 2 when racing is on.
breaker.threshold3Consecutive failures before an upstream is skipped.
breaker.cooldown30sHow long a skipped upstream stays out of rotation before one probe request is tried.
upstreams[]Ordered list of upstreams. Earlier entries are tried first.

Racing sends extra requests to the upstreams. Docker Hub enforces pull rate limits, so racing uses a bounded default of three concurrent attempts. Set race.enabled: false to opt out.

Upstreams

KeyDefaultDescription
idomittedOptional legacy label, unique within the registry. 1 to 63 letters, digits, - or _. URL is the identity; new configurations need no ID.
urlrequiredBase URL of a Registry v2 endpoint. http or https, with no query, fragment or credentials. A base path such as /hub is allowed.
disabledfalseKeep the upstream in the file but out of rotation.
timeouts.connect10sMaximum time to establish the connection: the TCP dial and the TLS handshake. Raise it for slow or proxied paths.
timeouts.first_byte5sMaximum wait for response headers. This is the cost of meeting a silent upstream.
timeouts.overall60sBound for metadata requests: manifests, tag lists and referrers. Blob transfers are not bounded by it.
auth.typenonenone, basic or token. See below.
auth.usernameUsername for basic, or for token when the token service requires one.
auth.passwordSecret reference, for example env:MIRROR_PASSWORD or file:/run/secrets/mirror. Literal passwords are rejected.
tls.ca_fileExtra CA certificate file for this upstream. It is added to the system roots.
tls.insecure_skip_verifyfalseDisable certificate verification for this upstream. Avoid this except for testing.

Authentication types

  • none: requests are sent without credentials.
  • basic: username and password are sent as HTTP Basic credentials with every request.
  • token: the upstream answers with a WWW-Authenticate: Bearer challenge. The proxy requests a token from the realm named in the challenge, caches it per scope until shortly before it expires, and retries the request once. Set username and password only when the token service requires them.

Credentials are scoped to their upstream. They are never sent to another upstream, and the client's own Authorization header is never forwarded.

Secret references

password accepts a reference, not a value:

ReferenceResolves to
env:NAMEThe value of environment variable NAME. It must be set and non-empty.
file:/pathThe contents of the file, with trailing line breaks removed.

References are resolved when the upstream is built, so a missing variable is reported at startup or on reload, not when the first request arrives.

Timing defaults in practice

With the defaults, a silent upstream first in the list costs about 5 seconds per request until its breaker opens. After three such failures the upstream is skipped for 30 seconds, then probed once. With racing enabled and the defaults, a silent upstream costs about 500 milliseconds, because the next upstream is started after that delay.

Changing the configuration

  • Through the CLI or admin API: changes are validated, saved, and applied without a restart. Validation failures leave the files and running configuration unchanged. New upstreams go in the main file; enable/disable choices are saved in local-overrides.yaml.
  • By hand: edit the file, then run oci-mirror-proxy reload or send SIGHUP to the process. A file that fails validation is rejected, and the running configuration is kept.
  • Listen address: a change to listen is logged, and takes effect after a restart.
  • In-flight transfers: reload does not interrupt existing HTTP requests or change their resume candidates. Later HTTP requests, including those in the same docker pull, use the new configuration.

Adaptive blob switching is enabled by default (10s window, 262144 bytes/s minimum, 5s probe timeout, 3 probe rounds). Set adaptive.enabled: false to disable it. Interrupted blob downloads resume automatically. These features do not split a blob into parallel chunks or change Docker layer-download concurrency.

On this page