A caching proxy for package registries. Speeds up package downloads by caching artifacts locally, reducing bandwidth usage and improving reliability.
| Registry | Language/Platform | URL Resolution | Handler | Completed |
|---|---|---|---|---|
| npm | JavaScript | Yes | Yes | ✓ |
| Cargo | Rust | Yes | Yes | ✓ |
| RubyGems | Ruby | Yes | Yes | ✓ |
| Go proxy | Go | Yes | Yes | ✓ |
| Hex | Elixir | Yes | Yes | ✓ |
| pub.dev | Dart | Yes | Yes | ✓ |
| PyPI | Python | Yes | Yes | ✓ |
| Maven | Java | Yes | Yes | ✓ |
| NuGet | .NET | Yes | Yes | ✓ |
| Composer | PHP | Yes | Yes | ✓ |
| Conan | C/C++ | Yes | Yes | ✓ |
| Conda | Python/R | Yes | Yes | ✓ |
| CRAN | R | Yes | Yes | ✓ |
| Alpine | Alpine Linux | No | No | ✗ |
| Arch | Arch Linux | No | No | ✗ |
| Chef | Chef | No | No | ✗ |
| Container | OCI | No | No | ✗ |
| Debian | Debian/Ubuntu | No | No | ✗ |
| Generic | Any | No | No | ✗ |
| Helm | Kubernetes | No | No | ✗ |
| RPM | RHEL/Fedora | No | No | ✗ |
| Swift | Swift | No | No | ✗ |
| Vagrant | Vagrant | No | No | ✗ |
# Build from source
go build -o proxy ./cmd/proxy
# Run with defaults (listens on :8080)
./proxy
# Run with custom settings
./proxy -listen :3000 -base-url https://proxy.example.comThe proxy is now running. Configure your package managers to use it.
Create or edit ~/.npmrc:
registry=http://localhost:8080/npm/
Or set per-project in .npmrc:
registry=http://localhost:8080/npm/
Or use environment variable:
npm_config_registry=http://localhost:8080/npm/ npm installCreate or edit ~/.cargo/config.toml:
[source.crates-io]
replace-with = "proxy"
[source.proxy]
registry = "sparse+http://localhost:8080/cargo/"Or set per-project in .cargo/config.toml in your project root.
Set the gem source in your Gemfile:
source "http://localhost:8080/gem"Or configure globally:
gem sources --add http://localhost:8080/gem/
bundle config mirror.https://rubygems.org http://localhost:8080/gemSet the GOPROXY environment variable:
export GOPROXY=http://localhost:8080/go,directOr in your shell profile for persistence.
Configure in ~/.hex/hex.config:
{default_url, <<"http://localhost:8080/hex">>}.Or set the environment variable:
export HEX_MIRROR=http://localhost:8080/hexSet the PUB_HOSTED_URL environment variable:
export PUB_HOSTED_URL=http://localhost:8080/pubConfigure pip to use the proxy:
pip install --index-url http://localhost:8080/pypi/simple/ package_nameOr set in ~/.pip/pip.conf:
[global]
index-url = http://localhost:8080/pypi/simple/Add to your ~/.m2/settings.xml:
<settings>
<mirrors>
<mirror>
<id>proxy</id>
<mirrorOf>central</mirrorOf>
<url>http://localhost:8080/maven/</url>
</mirror>
</mirrors>
</settings>Configure in nuget.config:
<configuration>
<packageSources>
<clear />
<add key="proxy" value="http://localhost:8080/nuget/v3/index.json" />
</packageSources>
</configuration>Or use the CLI:
dotnet nuget add source http://localhost:8080/nuget/v3/index.json -n proxyConfigure in composer.json:
{
"repositories": [
{
"type": "composer",
"url": "http://localhost:8080/composer"
}
]
}Or set globally:
composer config -g repositories.proxy composer http://localhost:8080/composerAdd the proxy as a remote:
conan remote add proxy http://localhost:8080/conan
conan remote disable conancenterOr configure in ~/.conan2/remotes.json.
Configure in ~/.condarc:
channels:
- http://localhost:8080/conda/main
- http://localhost:8080/conda/conda-forge
default_channels:
- http://localhost:8080/conda/mainOr set via command:
conda config --add channels http://localhost:8080/conda/mainSet the repository in R:
options(repos = c(CRAN = "http://localhost:8080/cran"))Or in ~/.Rprofile for persistence:
local({
r <- getOption("repos")
r["CRAN"] <- "http://localhost:8080/cran"
options(repos = r)
})The proxy can be configured via:
- Command line flags (highest priority)
- Environment variables
- Configuration file (YAML or JSON)
-config string Path to configuration file
-listen string Address to listen on (default ":8080")
-base-url string Public URL of this proxy (default "http://localhost:8080")
-storage string Path to artifact storage directory (default "./cache/artifacts")
-database string Path to SQLite database file (default "./cache/proxy.db")
-log-level string Log level: debug, info, warn, error (default "info")
-log-format string Log format: text, json (default "text")
-version Print version and exit
PROXY_LISTEN=:8080
PROXY_BASE_URL=http://localhost:8080
PROXY_STORAGE_PATH=./cache/artifacts
PROXY_DATABASE_PATH=./cache/proxy.db
PROXY_LOG_LEVEL=info
PROXY_LOG_FORMAT=textlisten: ":8080"
base_url: "http://localhost:8080"
storage:
path: "/var/cache/proxy/artifacts"
max_size: "10GB" # Optional: evict LRU when exceeded
database:
path: "/var/lib/proxy/cache.db"
log:
level: "info"
format: "text"
# Optional: override upstream URLs
upstream:
npm: "https://registry.npmjs.org"
cargo: "https://index.crates.io"Run with config file:
./proxy -config /etc/proxy/config.yamlStart the proxy server. This is the default command if none is specified.
proxy serve [flags]
proxy [flags] # same as 'proxy serve'Show cache statistics without running the server.
# Text output
proxy stats
# JSON output
proxy stats -json
# Custom database path
proxy stats -database /var/lib/proxy/cache.db
# Show top 20 most popular packages
proxy stats -popular 20Example output:
Cache Statistics
================
Packages: 45
Versions: 128
Artifacts: 128
Total size: 892.4 MB
Total hits: 1547
Packages by ecosystem:
npm 32
cargo 13
Most popular packages:
1. npm/lodash (342 hits, 24.7 KB)
2. npm/react (198 hits, 89.3 KB)
3. cargo/serde (156 hits, 234.1 KB)
Recently cached:
npm/express@4.18.2 (2024-01-15 14:32, 54.2 KB)
cargo/tokio@1.35.0 (2024-01-15 14:28, 412.8 KB)
| Endpoint | Description |
|---|---|
GET / |
Welcome message and endpoint list |
GET /health |
Health check (returns "ok" if healthy) |
GET /stats |
Cache statistics (JSON) |
GET /npm/* |
npm registry protocol |
GET /cargo/* |
Cargo sparse index protocol |
GET /gem/* |
RubyGems protocol |
GET /go/* |
Go module proxy protocol |
GET /hex/* |
Hex.pm protocol |
GET /pub/* |
pub.dev protocol |
GET /pypi/* |
PyPI simple/JSON API |
GET /maven/* |
Maven repository protocol |
GET /nuget/* |
NuGet V3 API |
GET /composer/* |
Composer/Packagist protocol |
GET /conan/* |
Conan C/C++ protocol |
GET /conda/* |
Conda/Anaconda protocol |
GET /cran/* |
CRAN (R) protocol |
{
"cached_artifacts": 142,
"total_size_bytes": 523456789,
"total_size": "499.2 MB",
"storage_path": "./cache/artifacts",
"database_path": "./cache/proxy.db"
}- Package manager requests package metadata from the proxy
- Proxy fetches metadata from upstream, rewrites artifact URLs to point at proxy
- Package manager requests artifact (tarball, crate, etc.)
- Proxy checks local cache:
- Cache hit: Serve from local storage
- Cache miss: Fetch from upstream, store locally, serve to client
- Subsequent requests for the same artifact are served from cache
┌─────────────┐ ┌─────────┐ ┌──────────┐
│ npm/cargo │────▶│ proxy │────▶│ upstream │
│ client │◀────│ │◀────│ registry │
└─────────────┘ └─────────┘ └──────────┘
│
▼
┌─────────┐
│ cache │
│ storage │
└─────────┘
Create /etc/systemd/system/proxy.service:
[Unit]
Description=git-pkgs proxy
After=network.target
[Service]
Type=simple
User=proxy
ExecStart=/usr/local/bin/proxy -config /etc/proxy/config.yaml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable proxy
sudo systemctl start proxyFROM golang:1.23-alpine AS build
WORKDIR /app
COPY . .
RUN go build -o proxy ./cmd/proxy
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=build /app/proxy /usr/local/bin/
EXPOSE 8080
VOLUME ["/data"]
CMD ["proxy", "-storage", "/data/artifacts", "-database", "/data/proxy.db"]Build and run:
docker build -t proxy .
docker run -p 8080:8080 -v proxy-data:/data proxyWhen running behind nginx, Apache, or another reverse proxy, set base_url to your public URL:
base_url: "https://proxy.example.com"nginx example:
server {
listen 443 ssl;
server_name proxy.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off;
}
}The proxy stores artifacts in the configured storage directory with this structure:
cache/artifacts/
├── npm/
│ └── lodash/
│ └── 4.17.21/
│ └── lodash-4.17.21.tgz
└── cargo/
└── serde/
└── 1.0.193/
└── serde-1.0.193.crate
Cache metadata is stored in an SQLite database. To clear the cache:
rm -rf ./cache/artifacts/*
rm ./cache/proxy.dbThe proxy will recreate the database on next start.
Requirements:
- Go 1.23 or later
git clone https://github.com/git-pkgs/proxy.git
cd proxy
go build -o proxy ./cmd/proxyRun tests:
go test ./...GPL-3.0-or-later