Building images

A container build fetches from two kinds of places: the registries its base images come from, and the package registries its RUN steps install from. Both have to go through SlowShield, and they are set up in different places: the base images in the container runtime, the dependencies in the Dockerfile.

1. Base images: the runtime

Configure the runtime once per machine, as on Container images:

Alternatively, name SlowShield in the Dockerfile: FROM slowshield.example.com/docker.io/library/python:3.13-slim. That works with any builder but ties the Dockerfile to your SlowShield.

2. Dependencies: the Dockerfile

A build container doesn't read your shell profile or your ~/.m2. Give each stage that installs something an ARG named like the tool's setting. An ARG reaches RUN as an environment variable and isn't kept in the image. With SlowShield as the default, every build uses it, and --build-arg can still override it:

LanguageIn the build stage
Python (pip)ARG PIP_INDEX_URL=https://slowshield.example.com/pypi/simple/
Python (uv)ARG UV_DEFAULT_INDEX=https://slowshield.example.com/pypi/simple/
JavaScript (npm, pnpm)ARG npm_config_registry=https://slowshield.example.com/npm/
GoARG GOPROXY=https://slowshield.example.com/go
Java (Maven)a settings.xml with the mirror, written in a RUN step (Java)
Java (Gradle)the init script in /root/.gradle/init.d/ (Java)
RustCargo's config.toml, written in a RUN step (Rust)

A complete example, a Go service on a distroless base:

FROM golang:1.25 AS build
ARG GOPROXY=https://slowshield.example.com/go
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app .

FROM gcr.io/distroless/static-debian12
COPY --from=build /app /app
ENTRYPOINT ["/app"]

Both FROM lines come through SlowShield via the runtime's configuration (step 1), the modules via the ARG. An ARG applies to the stage it is declared in: repeat it in each stage that installs something.

npm needs SlowShield's public name. SlowShield's tarball links are absolute, made from its public URL, so the build has to reach SlowShield under that name. With a real host name and HTTPS that's a given.

3. Check that nothing goes around it

Three checks, from quick to strict:

  1. Look at the dashboard. After a build, SlowShield's Packages page lists every base image and package it fetched. A dependency that doesn't show up came from somewhere else.
  2. Stop SlowShield and build again with --no-cache --pull. The pull and every install step should fail. If something still downloads, it goes around SlowShield.
  3. Build where SlowShield is the only thing reachable. A network without internet access that contains SlowShield, and a builder on it. Anything that isn't configured fails, instead of quietly going to the public registry. With Docker:
    # a network without internet access, joined by the container that serves
    # SlowShield's HTTPS (Caddy, in the Compose setup)
    docker network create --internal slowshield-only
    docker network connect --alias slowshield.example.com slowshield-only <caddy-container>
    
    # a builder on that network; buildkitd.toml from the Setup page
    docker buildx create --name slowshield-only --driver docker-container \
      --driver-opt network=slowshield-only --buildkitd-config buildkitd.toml
    
    # RUN steps don't use the network's DNS: pass SlowShield's address
    docker buildx build --builder slowshield-only --load \
      --add-host slowshield.example.com=$(docker inspect -f \
        '{{(index .NetworkSettings.Networks "slowshield-only").IPAddress}}' <caddy-container>) .
    In CI, the same idea is an egress allowlist that only contains SlowShield.

What we tested

On 2026-10-07, on macOS with Docker Desktop (Docker 29.8.1, containerd image store) and Podman 6.1, against a SlowShield serving the real registries. Six small applications, each with dependencies from its registry:

ApplicationBase imagesDependencies
Python, pippython:3.13-slimrequests and 4 more from PyPI
Python, uvpython:3.13-slim, ghcr.io/astral-sh/uvthe same, with uv
JavaScript, npmnode:22-slimexpress and 67 more from npm
Gogolang:1.25, gcr.io/distroless/static-debian12github.com/google/uuid, through the module proxy and the checksum database
Java, Mavenmaven:3.9-eclipse-temurin-21, mcr.microsoft.com/openjdk/jdk:21-distrolesscommons-lang3 and Maven's own plugins: 107 artifacts
Rust, Cargorust:1, gcr.io/distroless/cc-debian12itoa from crates.io

What we ran into, so you don't have to:

For agents: this page as Markdown · llms.txt · the SlowShield skill