Caching proxy for APT (Ubuntu/Debian), YUM (CentOS), and APK (Alpine). Speeds up repeated package downloads by serving them from a local cache. Drop-in replacement for apt-cacher-ng.
# Docker
docker run -d -p 3142:3142 -v apt-proxy-cache:/app/.aptcache tuzelko/apt-proxy
# Or download the binary from the releases page
./apt-proxyThe proxy starts immediately and benchmarks mirrors in the background, switching to the fastest one for your network once the benchmark completes.
| OS | Architectures |
|---|---|
| Linux | x86_64, x86_32, ARM64, ARM32v6, ARM32v7 |
| macOS | x86_64, Apple Silicon (ARM64) |
# One-off
http_proxy=http://your-host:3142 apt-get update
http_proxy=http://your-host:3142 apt-get install vim -y
# Persistent — /etc/apt/apt.conf.d/01proxy
Acquire::http::Proxy "http://your-host:3142";sed -i \
-e 's/mirrorlist.*$//' \
-e 's/#baseurl/baseurl/' \
-e 's#http://mirror.centos.org#http://your-host:3142#' \
/etc/yum.repos.d/CentOS-Base.repo
yum updatesed -i \
-e 's#mirror.centos.org#http://your-host:3142#g' \
-e 's/#baseurl/baseurl/' \
-e 's#\$releasever/#8-stream/#' \
/etc/yum.repos.d/CentOS-*
yum updatesed -i 's#https://.*.alpinelinux.org#http://your-host:3142#' /etc/apk/repositories
apk updatePriority (highest first): CLI flags → environment variables (APT_PROXY_*) → YAML file → defaults.
| Flag | Env var | Default | Description |
|---|---|---|---|
-host |
APT_PROXY_HOST |
0.0.0.0 |
Interface to bind |
-port |
APT_PROXY_PORT |
3142 |
Port to listen on |
-mode |
APT_PROXY_MODE |
all |
all, ubuntu, ubuntu-ports, debian, centos, alpine |
-cachedir |
APT_PROXY_CACHEDIR |
./.aptcache |
Cache directory |
-cache-max-size |
APT_PROXY_CACHE_MAX_SIZE |
10 |
Max cache size, GB (0 = unlimited, LRU eviction) |
-cache-ttl |
APT_PROXY_CACHE_TTL |
168 |
Cache TTL, hours (0 = no expiry) |
-cache-cleanup-interval |
APT_PROXY_CACHE_CLEANUP_INTERVAL |
60 |
Cleanup interval, minutes |
-ubuntu / -debian / etc. |
APT_PROXY_UBUNTU / … |
auto | Mirror URL or shortcut |
-distributions-config |
APT_PROXY_DISTRIBUTIONS_CONFIG |
(search paths) | Path to distributions.yaml |
-disable-builtin-distros |
APT_PROXY_DISABLE_BUILTIN_DISTROS |
false |
Disable built-in distributions; use only those defined in distributions.yaml |
-async-benchmark |
APT_PROXY_ASYNC_BENCHMARK |
true |
Benchmark mirrors in background on startup |
-api-key |
APT_PROXY_API_KEY |
— | Key for management API endpoints |
-tls / -tls-cert / -tls-key |
APT_PROXY_TLS_* |
— | HTTPS support |
-config |
APT_PROXY_CONFIG_FILE |
— | Path to YAML config file |
-debug |
APT_PROXY_DEBUG |
false |
Verbose logging |
-version |
— | — | Print version and exit |
cn:tsinghua · cn:ustc · cn:163 · cn:aliyun · cn:huaweicloud · cn:tencent
./apt-proxy --ubuntu=cn:tsinghua --debian=cn:ustcConfig file search paths: ./apt-proxy.yaml → /etc/apt-proxy/apt-proxy.yaml → ~/.config/apt-proxy/apt-proxy.yaml → ~/.apt-proxy.yaml
server:
host: 0.0.0.0
port: 3142
debug: false
cache:
dir: /var/cache/apt-proxy
max_size_gb: 20
ttl_hours: 168
cleanup_interval_min: 60
mirrors:
ubuntu: cn:tsinghua
debian: cn:ustc
security:
api_key: ${APT_PROXY_API_KEY}
enable_api_auth: trueMirror lists and distribution settings are defined in distributions.yaml. You can extend or override them without recompiling.
Search paths: ./config/distributions.yaml → ./distributions.yaml → /etc/apt-proxy/ → ~/.config/apt-proxy/
distributions:
- id: ubuntu
name: Ubuntu
type: 1
url_pattern: "/ubuntu/(.+)$"
benchmark_url: "dists/noble/main/binary-amd64/Release"
geo_mirror_api: "http://mirrors.ubuntu.com/mirrors.txt"
cache_rules:
- pattern: "deb$"
cache_control: "max-age=100000"
rewrite: true
mirrors:
official:
- "mirrors.tuna.tsinghua.edu.cn/ubuntu/"
aliases:
tsinghua: "mirrors.tuna.tsinghua.edu.cn/ubuntu/"After editing, reload without restart:
kill -HUP $(pgrep apt-proxy)
# or
curl -X POST -H "X-API-Key: $KEY" http://localhost:3142/api/mirrors/refresh
⚠️ Hot reload applies to distributions and mirrors only. Host/port, TLS, and API key require a full restart.
Any HTTP-based APT repository can be proxied and cached. Examples — Docker CE and Proxmox VE:
distributions:
- id: docker-debian
name: Docker CE (Debian)
type: 1
url_pattern: "/linux/debian/(.+)$"
benchmark_url: "dists/bookworm/stable/binary-amd64/Release"
cache_rules:
- pattern: "\\.deb$"
cache_control: "max-age=100000"
rewrite: true
mirrors:
official:
- "download.docker.com/linux/debian/"
- id: proxmox-ve
name: Proxmox VE
type: 1
url_pattern: "/debian/pve/(.+)$"
benchmark_url: "dists/bookworm/pve-no-subscription/binary-amd64/Release"
cache_rules:
- pattern: "\\.deb$"
cache_control: "max-age=100000"
rewrite: true
mirrors:
official:
- "download.proxmox.com/debian/pve/"Point sources.list at the proxy:
deb http://your-proxy:3142/linux/debian bookworm stable
deb http://your-proxy:3142/debian/pve bookworm pve-no-subscription
The same approach works for any APT-based repo: Grafana, Kubernetes, HashiCorp, etc.
💡 If you only need custom repositories and want to disable Ubuntu/Debian/CentOS/Alpine entirely, use
--disable-builtin-distros(orAPT_PROXY_DISABLE_BUILTIN_DISTROS=true).
docker run -d \
--name apt-proxy \
-p 3142:3142 \
-v apt-proxy-cache:/app/.aptcache \
tuzelko/apt-proxy
# Use inside another container
http_proxy=http://host.docker.internal:3142 apt-get install -y vimSee example/ for Docker Compose configurations.
Image tags: latest, 0, 0.11, 0.11.0 — all multi-arch (amd64, arm64, armv7).
Public: GET /healthz, /livez, /readyz, /version, /metrics (Prometheus).
Protected (require X-API-Key header or Authorization: Bearer <key>):
| Method | Path | Description |
|---|---|---|
| GET | /api/cache/stats |
Size, hit rate, item count |
| POST | /api/cache/purge |
Clear all cached items |
| POST | /api/cache/cleanup |
Remove stale entries |
| POST | /api/mirrors/refresh |
Reload distributions + re-benchmark |
curl -H "X-API-Key: your-key" http://localhost:3142/api/cache/statsThe /metrics endpoint exposes Prometheus metrics:
| Metric | Description |
|---|---|
apt_proxy_cache_* |
Hits, misses, size, item count, evictions |
apt_proxy_cache_upstream_* |
Upstream request duration and error rate |
apt_proxy_cache_request_duration_seconds |
Request latency by distro and cache status |
Health endpoints (/healthz, /readyz) are suitable for Kubernetes probes.
Client → apt-proxy → Cache hit → Client
↘ Cache miss → upstream mirror → Cache → Client
- URL rewriting — rewrites request URLs to the target mirror; no transparent forwarding
- Async benchmarking — mirrors benchmarked in background on startup (24h TTL); proxy serves immediately and upgrades once done
- Dynamic distributions — add any distro via
distributions.yamlwithout code changes - Retryable transport — 3 retries, exponential backoff (100ms initial, 2× multiplier, 2s max)
- LRU eviction — when
cache-max-sizeis exceeded, least recently used files are evicted first - State — thread-safe singleton (
sync.Once+sync.RWMutex), updated at startup and on hot reload
make build # build binary → ./apt-proxy
make test # run tests
make coverage # test + coverage summary
make coverage-html # open coverage report in browser
make vet # go vetPackages not being cached — verify the proxy URL is reachable from the client and the URL pattern in distributions.yaml matches the repository path.
Slow first download — expected: the first request populates the cache. Subsequent requests are served locally.
Cache growing too large — set --cache-max-size or call POST /api/cache/cleanup.
Debug logging:
./apt-proxy --debug
# or
APT_PROXY_DEBUG=true ./apt-proxy- lox/apt-proxy — original APT proxy implementation
- soulteary/apt-proxy — upstream fork with major feature additions
- apham0001/apt-proxy — fork base for this project
- bldrdash/apt-proxy — inspired
--async-benchmark,--versionflag, and systemd unit - athoune/apt-proxy — inspired embedded static assets
Maintainer: Eugene Frost · Repository: GitHub · Registry: Docker Hub
❤️ Issues and pull requests are welcome!

