The proxy can be configured via command line flags, environment variables, or a configuration file. Command line flags take precedence over environment variables, which take precedence over the configuration file.
Create a YAML or JSON file and pass it with -config:
proxy serve -config config.yamlSee config.example.yaml in the repository root for a complete example.
| Config | Environment | Flag | Default | Description |
|---|---|---|---|---|
listen |
PROXY_LISTEN |
-listen |
:8080 |
Address to listen on |
base_url |
PROXY_BASE_URL |
-base-url |
http://localhost:8080 |
Public URL for the proxy |
The proxy stores cached artifacts using gocloud.dev/blob, supporting local filesystem and S3-compatible storage.
storage:
url: "file:///var/cache/proxy"Or using the legacy path option:
storage:
path: "./cache/artifacts"| Config | Environment | Flag | Description |
|---|---|---|---|
storage.url |
PROXY_STORAGE_URL |
-storage-url |
Storage URL (file:// or s3://) |
storage.path |
PROXY_STORAGE_PATH |
-storage-path |
Local path (deprecated, use url) |
storage.max_size |
PROXY_STORAGE_MAX_SIZE |
- | Max cache size (e.g., "10GB") |
storage:
url: "s3://my-bucket"Configure credentials via environment variables:
export AWS_ACCESS_KEY_ID=your-key
export AWS_SECRET_ACCESS_KEY=your-secret
export AWS_REGION=us-east-1storage:
url: "s3://my-bucket?endpoint=http://localhost:9000&disableSSL=true&s3ForcePathStyle=true"The proxy supports SQLite (default) and PostgreSQL for storing package metadata.
database:
driver: "sqlite"
path: "./cache/proxy.db"| Config | Environment | Flag | Description |
|---|---|---|---|
database.driver |
PROXY_DATABASE_DRIVER |
-database-driver |
sqlite or postgres |
database.path |
PROXY_DATABASE_PATH |
-database-path |
SQLite file path |
database:
driver: "postgres"
url: "postgres://user:password@localhost:5432/proxy?sslmode=disable"| Config | Environment | Flag | Description |
|---|---|---|---|
database.url |
PROXY_DATABASE_URL |
-database-url |
PostgreSQL connection URL |
log:
level: "info"
format: "text"| Config | Environment | Flag | Values |
|---|---|---|---|
log.level |
PROXY_LOG_LEVEL |
-log-level |
debug, info, warn, error |
log.format |
PROXY_LOG_FORMAT |
-log-format |
text, json |
Override default upstream registry URLs:
upstream:
npm: "https://registry.npmjs.org"
cargo: "https://index.crates.io"
cargo_download: "https://static.crates.io/crates"Configure authentication for private upstream registries. Auth is matched by URL prefix, and credentials can reference environment variables using ${VAR_NAME} syntax.
Used by npm, GitHub Package Registry, and many other registries:
upstream:
auth:
"https://registry.npmjs.org":
type: bearer
token: "${NPM_TOKEN}"
"https://npm.pkg.github.com":
type: bearer
token: "${GITHUB_TOKEN}"Used by PyPI, Artifactory, and others:
upstream:
auth:
"https://pypi.org":
type: basic
username: "__token__"
password: "${PYPI_TOKEN}"
"https://artifactory.mycompany.com":
type: basic
username: "deploy"
password: "${ARTIFACTORY_PASSWORD}"For registries that use non-standard authentication headers:
upstream:
auth:
"https://maven.mycompany.com":
type: header
header_name: "X-Auth-Token"
header_value: "${MAVEN_TOKEN}"Auth configs are matched by URL prefix. The longest matching prefix wins, so you can configure different credentials for different paths:
upstream:
auth:
# All requests to this registry
"https://registry.mycompany.com":
type: bearer
token: "${REGISTRY_TOKEN}"
# Override for a specific scope
"https://registry.mycompany.com/@private":
type: bearer
token: "${PRIVATE_TOKEN}"The cooldown feature hides package versions published too recently, giving the community time to spot malicious releases before they reach your projects. When a version is within its cooldown period, it's stripped from metadata responses so package managers won't install it.
cooldown:
default: "3d"
ecosystems:
npm: "7d"
cargo: "0"
packages:
"pkg:npm/lodash": "0"
"pkg:npm/@babel/core": "14d"| Config | Environment | Description |
|---|---|---|
cooldown.default |
PROXY_COOLDOWN_DEFAULT |
Global default cooldown |
cooldown.ecosystems |
- | Per-ecosystem overrides |
cooldown.packages |
- | Per-package overrides (keyed by PURL) |
Durations support days (7d), hours (48h), and minutes (30m). Set to 0 to disable.
Resolution order: package override, then ecosystem override, then global default. This lets you set a conservative default while exempting trusted packages.
Currently supported for npm, PyPI, pub.dev, Composer, Cargo, NuGet, Conda, RubyGems, and Hex. These ecosystems include publish timestamps in their metadata.
Note: Hex cooldown requires disabling registry signature verification since the proxy re-encodes the protobuf payload without the original signature. Set HEX_NO_VERIFY_REPO_ORIGIN=1 or configure your repo with no_verify: true.
By default the proxy fetches metadata fresh from upstream on every request. Enable cache_metadata to store metadata responses in the database and storage backend for offline fallback. When upstream is unreachable, the proxy serves the last cached copy. ETag-based revalidation avoids re-downloading unchanged metadata.
cache_metadata: trueOr via environment variable: PROXY_CACHE_METADATA=true.
The proxy mirror command always enables metadata caching regardless of this setting.
When metadata caching is enabled, metadata_ttl controls how long a cached response is considered fresh before revalidating with upstream. During the TTL window, cached metadata is served directly without contacting upstream, reducing latency and upstream load.
metadata_ttl: "5m" # defaultOr via environment variable: PROXY_METADATA_TTL=10m.
Set to "0" to always revalidate with upstream (ETag-based conditional requests still avoid re-downloading unchanged content).
When upstream is unreachable and the cached entry is past its TTL, the proxy serves the stale cached copy with a Warning: 110 - "Response is Stale" header so clients can tell the data may be outdated.
The /api/mirror endpoints are disabled by default. Enable them to allow starting mirror jobs via HTTP:
mirror_api: trueOr via environment variable: PROXY_MIRROR_API=true.
When disabled, the endpoints are not registered and return 404.
The proxy mirror command pre-populates the cache from various sources. It accepts the same storage and database flags as serve.
| Flag | Default | Description |
|---|---|---|
--sbom |
Path to CycloneDX or SPDX SBOM file | |
--concurrency |
4 |
Number of parallel downloads |
--dry-run |
false |
Show what would be mirrored without downloading |
--config |
Path to configuration file | |
--storage-url |
Storage URL | |
--database-driver |
Database driver | |
--database-path |
SQLite database file | |
--database-url |
PostgreSQL connection URL |
Positional arguments are treated as PURLs:
proxy mirror pkg:npm/lodash@4.17.21 pkg:cargo/serde@1.0.0docker compose updocker compose --profile postgres updocker compose --profile s3 uplisten: ":8080"listen: ":8080"
base_url: "https://proxy.example.com"
storage:
url: "s3://my-cache-bucket"
max_size: "100GB"
database:
driver: "postgres"
url: "postgres://proxy:secret@db.example.com:5432/proxy?sslmode=require"
log:
level: "info"
format: "json"listen: ":8080"
base_url: "http://localhost:8080"
upstream:
npm: "https://npm.pkg.github.com"
auth:
npm:
type: bearer
token: "${GITHUB_TOKEN}"