A go vrepo serves the GOPROXY protocol at /go/<vrepo>/. Its upstreams
are ordered rules matched by module path (GOPRIVATE-style patterns);
each one relays to another module proxy, or builds .info/.mod/.zip,
@v/list and @latest itself from a git repository: through the Gitea
or GitLab REST API, or over git smart-HTTP (go-git) for any other
host, without a git binary. Module layout follows the go command:
<dir>/vX.Y.Z tags, /vN major subdirectories, +incompatible, nested
modules and vendor/ left out of zips. go.mod is served as committed,
so forks keeping their upstream module path work through replace.
Repo templates map captured pattern elements to repository paths
({1}, {2}...). vanity_hosts answers ?go-get=1 with a go-import meta
tag pointing at this proxy or at the repository.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
280 lines
11 KiB
YAML
280 lines
11 KiB
YAML
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 <token>` or a `?token=<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 with multiple upstreams, tried in order: a 404, 401, 403, 429,
|
|
# 502, 503, 504 or connection error falls through to the next one, any
|
|
# other status is returned as-is. Here a remote tiny-proxy goes first and
|
|
# Docker Hub itself second, so pulls come from the remote cache when it is
|
|
# up and go direct when it is not. A url whose path starts with /v2/ is a
|
|
# repository root — another tiny-proxy's vrepo — and the image path is
|
|
# appended to it as-is (no second /v2/, no library/: the remote adds that).
|
|
# docker pull localhost:8080/hub-chained/library/nginx:1.27
|
|
- name: hub-chained
|
|
type: docker
|
|
upstreams:
|
|
- url: https://proxy.example.com/v2/dockerhub
|
|
# A dead remote should cost nothing: skip the connection-failure
|
|
# retries and fall through to Docker Hub at once.
|
|
retry: { attempts: 0 }
|
|
- url: https://registry-1.docker.io
|
|
auth:
|
|
mode: public
|
|
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 docker, debian and
|
|
# pacman, without filters). Upstreams are tried in order: a 404, 401, 403,
|
|
# 429, 502, 503, 504 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
|
|
# A path every upstream answered 404 for is answered 404 from memory
|
|
# for this long instead of walking the list again. Default 10m;
|
|
# negative disables.
|
|
not_found_ttl: 10m
|
|
|
|
# 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: <url>/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 <url>/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,
|
|
# 429, 502, 503, 504 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
|
|
# <repo>.db / <repo>.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
|
|
|
|
# Go module proxy: export GOPROXY=http://localhost:8080/go/go
|
|
# Upstreams are ordered rules by module path (GOPRIVATE-style patterns).
|
|
# Git-backed kinds (gitea, gitlab, git) build .info/.mod/.zip from the
|
|
# repository themselves, no git binary needed; goproxy relays to another
|
|
# module proxy. Modules built from private repos are unknown to the public
|
|
# checksum database: set GONOSUMDB (or GOPRIVATE) for their patterns.
|
|
- name: go
|
|
type: go
|
|
# Answer ?go-get=1 for these hosts with a go-import meta tag.
|
|
# vanity_hosts: [go.example.com]
|
|
upstreams:
|
|
# go.example.com/team/lib[/sub][/vN] -> Team/lib on Gitea.
|
|
# - kind: gitea
|
|
# modules: ["go.example.com/team/*"]
|
|
# url: https://gitea.example.com
|
|
# repo: "Team/{1}"
|
|
# password: gitea_token # read:repository scope
|
|
# Nested groups work: {1}/{2} -> group/project.
|
|
# - kind: gitlab
|
|
# modules: ["gitlab.example.com/*/*"]
|
|
# url: https://gitlab.example.com
|
|
# password: glpat-xxxx # sent as Bearer
|
|
# Any smart-HTTP git server.
|
|
# - kind: git
|
|
# modules: ["git.example.com/*"]
|
|
# url: https://git.example.com
|
|
# repo: "{1}.git"
|
|
# go_get: git # vanity answer points at the repo
|
|
- url: https://proxy.golang.org
|
|
auth:
|
|
mode: public
|
|
cache:
|
|
metadata_ttl: 5m # @v/list, @latest, branch queries
|