pkgproxy is a Web server that serves Linux packages for various repository
types (RPM, DEB, ...) from a local cache. It can be used as a central package
server in a local network. Packages not available in the local cache will be
fetched transparently from configurable upstream mirrors.
Build and run the code locally for testing:
PKGPROXY_CONFIG=./configs/pkgproxy.yaml go run github.com/ganto/pkgproxy serveRun the application via a container engine (e.g. Podman):
- Using the default repository configuration:
podman run --rm -p 8080:8080 --volume ./cache:/ko-app/cache:z ghcr.io/ganto/pkgproxy serve --host 0.0.0.0 --config /var/run/ko/pkgproxy.yaml- Mounting your own local
pkgproxy.yaml:
podman run --rm -p 8080:8080 --volume ./cache:/ko-app/cache:z --volume ./pkgproxy.yaml:/ko-app/pkgproxy.yaml ghcr.io/ganto/pkgproxy serve --host 0.0.0.0 --config /ko-app/pkgproxy.yaml| Flag | Env Variable | Default | Description |
|---|---|---|---|
--config, -c |
PKGPROXY_CONFIG |
./pkgproxy.yaml |
Path to the repository config file |
--cachedir |
cache |
Path to the local cache directory | |
--host |
localhost |
Listen address | |
--port |
8080 |
Listen port | |
--public-host |
PKGPROXY_PUBLIC_HOST |
Public hostname (or host:port) shown in landing page config snippets. When set, the listen port is not appended. Useful when running behind a reverse proxy. |
|
--debug |
false |
Enable debug logging |
An example repository configuration can be found at configs/pkgproxy.yaml.
Each repository supports the following options:
| Key | Required | Description |
|---|---|---|
suffixes |
yes | File suffixes that are eligible for caching (e.g. .rpm, .deb). Use "*" to cache all files. |
exclude |
no | List of file names to exclude from caching, even when they match a suffix. Useful with the "*" wildcard suffix. |
mirrors |
yes | Ordered list of upstream mirror URLs |
retries |
no | Number of attempts per mirror before moving to the next one (default: 1) |
Some upstream mirrors (e.g. download.fedoraproject.org) act as redirectors that
send clients to a randomly selected mirror via HTTP 302. If the selected mirror is
temporarily unavailable and responds with a 5xx error, pkgproxy can automatically
retry the request to the same redirector, which will typically redirect to a
different, working mirror.
To enable this, set retries to a value greater than 1:
repositories:
fedora:
suffixes:
- .rpm
mirrors:
- https://download.fedoraproject.org/pub/fedora/linux/
retries: 3With retries: 3, pkgproxy will attempt each mirror up to 3 times before moving
on to the next one. An exponential backoff is applied between retry attempts
(1s, 2s, 4s, ...). Only 5xx (server error) responses trigger a retry — client
errors like 404 are returned immediately.
When using the wildcard suffix "*" to cache all files, certain files (such as
metadata or timestamps) should not be cached because they change frequently. The
exclude option lets you list file names that will always be fetched from
upstream, bypassing the cache:
repositories:
gentoo:
suffixes:
- "*"
exclude:
- layout.conf
- timestamp.mirmon
- timestamp.dev-local
mirrors:
- https://distfiles.gentoo.org/Files whose name matches an entry in the exclude list are served directly from
the upstream mirror without being stored in the local cache.
With the provided configuration a number of Linux distributions are handled. See below where and how the clients must be adjusted to use your instance of pkgproxy. Replace <pkgproxy> with the host name of the pkgproxy instance:
E.g. /etc/yum.repos.d/almalinux-baseos.repo (adjust other repositories accordingly):
[baseos]
# mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/baseos
baseurl=http://<pkgproxy>:8080/almalinux/$releasever/BaseOS/$basearch/os/
/etc/pacman.d/mirrorlist:
Server = http://<pkgproxy>:8080/archlinux/$repo/os/$arch
E.g. Debian 13 Trixie: /etc/apt/sources.list (substitute your release codename):
deb http://<pkgproxy>:8080/debian trixie main contrib non-free non-free-firmware
deb http://<pkgproxy>:8080/debian trixie-updates main contrib non-free non-free-firmware
deb http://<pkgproxy>:8080/debian trixie-backports main contrib non-free non-free-firmware
deb http://<pkgproxy>:8080/debian-security trixie-security main contrib non-free non-free-firmware
- CentOS 7:
/etc/yum.repos.d/CentOS-Base.repo(adjust other repositories accordingly):
[base]
# mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra
baseurl=http://<pkgproxy>:8080/centos/$releasever/os/$basearch/
- CentOS Stream 8:
/etc/yum.repos.d/CentOS-Stream-BaseOS.repo(adjust other repositories accordingly):
[baseos]
# mirrorlist=http://mirrorlist.centos.org/?release=$stream&arch=$basearch&repo=BaseOS&infra=$infra
baseurl=http://<pkgproxy>:8080/centos/$stream/BaseOS/$basearch/os/
- CentOS Stream 9:
/etc/yum.repos.d/centos.repo(adjust other repositories accordingly):
[baseos]
# metalink=https://mirrors.centos.org/metalink?repo=centos-baseos-$stream&arch=$basearch&protocol=https,http
baseurl=http://<pkgproxy>:8080/centos-stream/$stream/BaseOS/$basearch/os/
Can be used for any type of RPM-based enterprise distribution. E.g. /etc/yum.repos.d/epel.repo (adjust other repositories accordingly):
[epel]
# metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-$releasever&arch=$basearch
baseurl=http://<pkgproxy>:8080/epel/$releasever/Everything/$basearch/
/etc/portage/make.conf:
GENTOO_MIRRORS="http://<pkgproxy>:8080/gentoo"
/etc/yum.repos.d/fedora.repo (adjust other repositories accordingly):
[fedora]
# metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch
baseurl=http://<pkgproxy>:8080/fedora/releases/$releasever/Everything/$basearch/os/
/etc/yum.repos.d/_copr:copr.fedorainfracloud.org:<user>:<repo>.repo (replace <user> and <repo> with the corresponding COPR repository):
[copr:copr.fedorainfracloud.org:<user>:<repo>]
# baseurl=https://download.copr.fedorainfracloud.org/results/<user>/<repo>/fedora-$releasever-$basearch/
baseurl=http://<pkgproxy>:8080/copr/<user>/<repo>/fedora-$releasever-$basearch/
For Enterprise distributions the URL suffix epel-$releasever-$basearch must be used.
/etc/yum.repos.d/rocky.repo (adjust other repositories accordingly):
[baseos]
# mirrorlist=https://mirrors.rockylinux.org/mirrorlist?arch=$basearch&repo=BaseOS-$releasever$rltype
baseurl=http://<pkgproxy>:8080/rockylinux/$releasever/BaseOS/$basearch/os/
E.g. Ubuntu 24.04 Noble Numbat: /etc/apt/sources.list (substitute your release codename):
deb http://<pkgproxy>:8080/ubuntu noble main restricted universe multiverse
deb http://<pkgproxy>:8080/ubuntu noble-updates main restricted universe multiverse
deb http://<pkgproxy>:8080/ubuntu-security noble-security main restricted universe multiverse
End-to-end tests validate pkgproxy against real package managers running in containers. They require either Podman or Docker.
Run all e2e tests:
make e2eRun tests for a specific distribution:
make e2e DISTRO=fedoraRun tests for a specific distribution and release:
make e2e DISTRO=fedora RELEASE=42Supported DISTRO values: fedora, centos-stream, almalinux, rockylinux, debian, ubuntu, archlinux, gentoo.
When adding support for a new Linux distribution, corresponding e2e tests should be added as well.
Build a container image locally using ko:
make image-buildThis builds a single-platform image for your host architecture and loads it into the local container runtime via ko.local.
To use Podman instead of Docker, point DOCKER_HOST to the Podman socket:
DOCKER_HOST=unix://$(podman info --format '{{.Host.RemoteSocket.Path}}') make image-build- Rename the
[Unreleased]section inCHANGELOG.mdto[v<version>] - <date>and add a new empty[Unreleased]section above it. - Commit the changelog update.
- Push a version tag (e.g.
git tag v0.1.0 && git push origin v0.1.0).
The tag push triggers the release workflow which builds a versioned container image, signs it with cosign, and creates a GitHub Release with notes extracted from CHANGELOG.md.
The pkgproxy code was written and is maintained by: