6.6 KiB
Using proxy in Docker builds
Docker builds can share proxy's package cache even when their builders and local package caches are new. Configure the package manager inside the build to use proxy. Cached archives still need to be downloaded from proxy and installed; native extensions still need to be compiled.
Run a persistent proxy
Run proxy outside the image build and keep its database and artifact storage between builds. For a proxy running on your development machine:
PROXY_CACHE_METADATA=true proxy serve \
-listen :8080 \
-base-url http://host.docker.internal:8080 \
-storage-url "file://$PWD/proxy-cache/artifacts" \
-database-path ./proxy-cache/proxy.db
Docker Desktop provides host.docker.internal for reaching the host. On Linux
Docker Engine, add the host mapping when building:
docker build \
--add-host host.docker.internal=host-gateway \
--build-arg PROXY_URL=http://host.docker.internal:8080 \
-t app .
localhost inside a build refers to the build container. Both the build's proxy
URL and proxy's base_url must be reachable from that container, since some
registry responses contain download URLs derived from base_url. The proxy
must listen on an interface reachable from the builder, with access restricted
to the machines that should use it.
For CI or a remote builder, use a persistent service reachable from the builder,
such as https://proxy.example.com, and set proxy's base_url to that address.
Use the same service across jobs and pass its URL with --build-arg PROXY_URL=....
Creating an empty proxy for every job loses the shared cache unless its database
and storage persist. If proxy runs in a container, mount persistent storage for
both. See configuration for storage and deployment options.
Metadata caching is optional, but avoids repeated metadata fetches within
metadata_ttl. Artifact caching is enabled by default. Proxy's configured
cooldown, denylist and scanning policies also apply to Docker builds.
npm
In your Node build stage, copy the manifests before application source so an unchanged dependency set can reuse Docker's installation layer:
ARG PROXY_URL
COPY package.json package-lock.json ./
RUN npm ci --no-audit --no-fund --registry="${PROXY_URL:?PROXY_URL build arg required}/npm/"
COPY . .
Pass PROXY_URL without a trailing slash. With Docker Desktop:
docker build \
--build-arg PROXY_URL=http://host.docker.internal:8080 \
-t app .
Keep node_modules out of the build context so COPY . . does not overwrite
the installed dependencies. Lockfiles with custom registry or download URLs
may still direct requests to those hosts; check proxy's access log when using
private registries or non-registry dependencies. --no-audit skips the audit
request, which proxy would otherwise forward upstream.
Ruby / Bundler
Keep https://rubygems.org as the source in your Gemfile and configure its mirror
inside the Ruby build stage. Install any system packages needed to compile
native extensions before this step:
ARG PROXY_URL
COPY Gemfile Gemfile.lock ./
RUN bundle config set --local mirror.https://rubygems.org "${PROXY_URL:?PROXY_URL build arg required}/gem" \
&& bundle install \
&& bundle config unset --local mirror.https://rubygems.org
COPY . .
Use the same build command as the npm example. The mirror setting is removed
after installation so it is not retained in the image, but the URL still
appears in docker history because Docker records the build args used by RUN
lines. Keep credentials out of PROXY_URL. Exclude host Bundler
configuration and installed gems from the build context. Copy any local path
dependencies required by the Gemfile before bundle install.
The RubyGems mirror covers gems fetched from that source. Git dependencies and
other gem sources need their own configuration. Without
mirror.https://rubygems.org.fallback_timeout, bundle install fails if proxy
is unreachable. Setting it lets Bundler fall back to rubygems.org, so this setup
does not enforce network isolation either way.
Layer caches and package caches
Keep existing Docker caching optimizations. If Docker reuses the installation layer, the package manager does not run and proxy receives no requests. A BuildKit cache mount can also preserve downloaded packages when that layer must be rebuilt. For example, the npm step can use:
RUN --mount=type=cache,target=/root/.npm \
npm ci --no-audit --no-fund --registry="${PROXY_URL:?PROXY_URL build arg required}/npm/"
Cache mounts are stored by the builder and are excluded from ordinary
--cache-to exports. Proxy can supply packages when a fresh builder has neither
the installation layer nor the package-manager cache. See Docker's
cache guidance and
cache-mount reference.
To check the configuration, enable proxy's JSONL log with -access-log and
rebuild with docker build --no-cache. Look for requests to /npm/ or /gem/,
then repeat and check proxy_cache_hits_total on /metrics. Cache mounts still
retain their contents with --no-cache; omit them when testing proxy cache
hits. Upstream metadata revalidation can still occur.
Base-image pulls
Package-manager configuration affects commands such as npm ci and
bundle install. To cache images referenced by FROM, configure a registry
mirror in the builder as well. For a proxy served at
https://proxy.example.com, put this in buildkitd.toml:
[registry."docker.io"]
mirrors = ["proxy.example.com"]
Create a Buildx builder with that configuration and select it explicitly:
docker buildx create --name proxy-builder \
--driver docker-container \
--buildkitd-config ./buildkitd.toml
docker buildx build --builder proxy-builder \
--build-arg PROXY_URL=https://proxy.example.com \
--load -t app .
For local Docker Desktop testing over HTTP, the configuration is:
[registry."docker.io"]
mirrors = ["host.docker.internal:8080"]
[registry."host.docker.internal:8080"]
http = true
The registry mirror address must be reachable from the BuildKit daemon itself.
The build's --add-host flag only adds a mapping inside build containers. A
separate Buildx builder has its own registry configuration; setting Docker
Engine's registry-mirrors does not configure that builder. See Docker's
BuildKit configuration guide.
Proxy's OCI endpoint is pull-only. BuildKit registry-cache exports with
--cache-to type=registry need a separate writable registry. Other cache export
backends can be used alongside proxy.