listen: ":8080" storage: # localfs | s3 | none. "none" disables caching entirely: every request is # proxied straight through to the upstream and nothing is stored. type: localfs localfs: root: ./data # s3: # bucket: my-cache # region: us-east-1 # endpoint: http://localhost:9000 # optional, for MinIO etc. # access_key: minioadmin # secret_key: minioadmin # prefix: cache # GET /metrics serves Prometheus text exposition: per-vrepo request counts, # durations and cache hit/miss/error counters; per-upstream attempts, response # statuses, byte totals (payload and real socket bytes) and timings (connect, # TLS handshake, time-to-first-byte, body); and Go runtime stats. Storage # sizes are deliberately absent: listing a large store on every scrape is too # expensive — use the backend's own accounting (`du`, bucket metrics) instead. # Empty token (the default) leaves the endpoint open; set one to require # `Authorization: Bearer ` or a `?token=` query param. # metrics: # token: "" # Every request is given a trace id (adopted from the caller's X-Request-Id or # traceparent when it sent one), echoed back in X-Request-Id, attached to every # log line the request produces, and passed on to the upstreams it calls — so # one grep follows a request end to end, across chained proxies included. # log: # level: info # debug | info | warn | error # format: text # text | json # requests: true # one line when a client request arrives and one when it ends # upstream: true # one line per outbound attempt against an upstream vrepos: # docker pull localhost:8080/dockerhub/library/nginx:1.27 - name: dockerhub type: docker upstream: url: https://registry-1.docker.io # username: myuser # optional Docker Hub credentials # password: mytoken # proxy: socks5://127.0.0.1:1080 auth: mode: public # public | password | users cache: manifest_ttl: 5m # docker pull localhost:8080/ghcr/org/image:tag (authenticated repo example) - name: ghcr type: docker upstream: url: https://ghcr.io username: github-user password: ghp_token auth: mode: password # any login + this password password: s3cret cache: manifest_ttl: 1m # https://localhost:8080/maven/central/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar - name: central type: maven upstream: url: https://repo1.maven.org/maven2 auth: mode: users users: alice: pw1 bob: pw2 cache: metadata_ttl: 5m # Maven with multiple upstreams (also supported for debian and pacman, # without filters). Upstreams are tried in order: a 404, 401, 403 or # connection error falls through to the next # one, any other status is returned as-is. Each upstream may restrict what it # serves with allowlist filters: # groups: wildcard group prefixes; "com.google.*" covers the # whole com/google tree ('*' crosses dots/slashes) # artifacts: wildcard artifact names, e.g. "my-lib-*" # snapshots_only: only -SNAPSHOT paths are requested from it # An upstream with no filters matches everything. Per-upstream # credentials and egress proxy work the same as the single-upstream form. # Any upstream (of any type, single or listed) may set `cache: false` to # be proxied through without being stored here — see the chained example # below. - name: maven-all type: maven upstreams: - url: https://corp.example.com/maven-snapshots username: deploy password: s3cret snapshots_only: true - url: https://maven.google.com groups: ["com.google.*"] - url: https://repo1.maven.org/maven2 # `attempts` covers three failures, each waiting the upstream's # Retry-After or a backoff when it sends none, at most max_wait per # wait: a 429 (Too Many Requests), a connection that fails before any # response arrives, and a transfer that dies mid-body (re-requested # with a byte range and continued where it stopped). Once the attempts # are spent the request falls through to the next upstream. # Defaults: attempts 2, max_wait 5s; attempts 0 disables. retry: attempts: 3 max_wait: 10s auth: mode: public cache: metadata_ttl: 5m # Chaining: a vrepo may use another vrepo of this proxy as an upstream. # `cache: false` keeps the aggregator from storing a second copy of what # the target vrepo already caches — the bytes live in `central` only, and # `deps` just routes. Reads still hit `deps`'s own cache if something was # stored there earlier by a cacheable upstream. Chained upstreams are # ordinary HTTP requests: add username/password if the target vrepo is # not `auth.mode: public`, and never let a chain loop back on itself. - name: deps type: maven upstreams: - url: https://mvn.example.com # cached here as usual - url: http://localhost:8080/maven/central cache: false # cached in the central vrepo auth: mode: public cache: metadata_ttl: 5m # curl -LO http://localhost:8080/github/gh/golang/go/latest.json # curl -LO http://localhost:8080/github/gh/golang/go/latest/go1.23.0.linux-amd64.tar.gz - name: gh type: github # upstream.url defaults to https://github.com (API: api.github.com). # For GitHub Enterprise set your instance URL (API: /api/v3). upstream: url: https://github.com # username: github-user # optional; PAT raises API rate limits # password: ghp_token # and unlocks private repos auth: mode: public # allow restricts which owner/repo pairs may be proxied (path.Match # globs). Omit or leave empty to allow everything. allow: - "golang/*" - "myorg/mytool" # redirect: true (default) sends /latest and /snapshot to the resolved # tag path with a 302; false serves content under the alias URL. # redirect: false cache: metadata_ttl: 5m # release metadata + alias resolution # Same, but with per-vhost users and an authenticated upstream (private # repos), serving aliases in place instead of redirecting. - name: gh-private type: github upstream: url: https://github.com username: github-user password: ghp_token auth: mode: users users: carol: pw3 redirect: false cache: metadata_ttl: 1m # Gitea releases: same URL grammar as the github type under /gitea/. # There is no default upstream — point url at your instance (its API is # served at /api/v1). # curl -LO http://localhost:8080/gitea/gt/myorg/mytool/latest/mytool_linux-amd64 - name: gt type: gitea upstream: url: https://gitea.example.com # username: gitea-user # optional; token unlocks private repos # password: gitea_token auth: mode: public # allow: ["myorg/*"] # redirect: false cache: metadata_ttl: 5m # Debian / APT repo. apt sources.list: deb http://localhost:8080/debian/deb bookworm main # Open /ui/debian/deb/ for a generated sources.list line per distro version # (bookworm, trixie, noble, ...) with each suite's components and # architectures, probed from the mirror and memoized for metadata_ttl. # Metadata under dists/** (Release, InRelease, Packages.*, ...) is cached # for metadata_ttl; pool/** and by-hash/** are content-addressed and # cached forever. Multiple upstreams act as mirrors: a 404, 401, 403 or # connection error falls through to the next one (they must serve the # same archive layout). - name: deb type: debian upstreams: - url: https://deb.debian.org/debian - url: http://ftp.us.debian.org/debian auth: mode: public cache: metadata_ttl: 5m # dists/** freshness; packages immutable # Pacman (Arch) mirror. pacman.conf: Server = http://localhost:8080/pacman/arch/$repo/os/$arch # .db / .files (any compression, plus .sig) and lastupdate # are cached for metadata_ttl; *.pkg.tar.* packages are immutable. - name: arch type: pacman upstream: url: https://geo.mirror.pkgbuild.com auth: mode: public # pacman has no native HTTP-auth support cache: metadata_ttl: 5m