Extend the local cache stack with three independent capabilities: 1. Inner-`dockerd` build coverage for HTTP-only package mirrors. `roles/sys-svc-compose/files/compose.py` (the runner's `compose` wrapper) auto-generates a transient `compose.cache.override.yml` per app when `INFINITO_PACKAGE_CACHE_FRONTEND_IP` is exported. The override pins `build.extra_hosts` for `deb.debian.org`, `archive.ubuntu.com`, and `security.ubuntu.com` to the frontend. `pull.py` now delegates to the wrapper so build/pull operations see the same `-f` set the wrapper auto-detects. nginx in the frontend gains a port-80 listener with path rewrites for those hostnames, and `package.sh` registers two new Nexus proxies (`apt-debian-security`, `apt-ubuntu-security`) for the security suites whose upstream URL prefix differs from the main archive. Alpine is intentionally excluded from the inner-build extra_hosts: 3.18+ defaults to HTTPS, and inner-`dockerd` build containers do not import the frontend CA. Routing apk through the frontend would trip TLS verification. 2. General cache freshness env-var. `scripts/meta/env/cache/package.sh` exports `INFINITO_PACKAGE_CACHE_MAX_AGE_MIN` (default `129600`, i.e. 90 days). The bootstrap helper consumes it for every Nexus proxy's `contentMaxAge`, `metadataMaxAge`, and `negativeCache.timeToLive`, replacing the previous 1440-minute literal. Lower the value when chasing fast-moving upstreams. 3. SPOT cache documentation at `docs/contributing/environment/cache.md`. `compose.yml.md`, `variables.md`, the per-service READMEs, and `requirements/012-...` now link to it instead of restating the stack's behaviour. Net documentation lines drop substantially while the cache page becomes the single user-facing reference. Also: code-comment cleanup per `docs/contributing/documentation.md` guidelines across all touched .py / .sh / .yml files. Multi-paragraph docstrings, banner separators, and restate-the-obvious comments are removed; one-line WHY comments remain where the rationale is not already visible in the code. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
9.0 KiB
Local Caches 📦
Three local cache services accelerate re-deploys, reduce upstream traffic, and harden the development workflow against transient upstream outages. They share one compose profile (cache) and one override file (compose/cache.override.yml) and are gated together by profile.py: active on developer machines, inactive on CI runners.
Services 🧩
Registry Cache 🐳
registry-cache runs rpardini/docker-registry-proxy and transparently MITMs every Docker image pull from the inner dockerd, regardless of upstream registry (docker.io, ghcr.io, mcr.microsoft.com, …). The runner trusts the proxy CA via registry-ca.sh.
Pulls only. Pushes are not intercepted.
Package Cache 📚
package-cache runs sonatype/nexus3 and exposes pull-through proxies for package-manager downloads:
| Format | Repo names | Upstream |
|---|---|---|
apt |
apt-debian, apt-debian-security, apt-ubuntu, apt-ubuntu-security |
deb.debian.org, archive.ubuntu.com, security.ubuntu.com |
pypi |
pypi-proxy |
pypi.org (incl. files.pythonhosted.org) |
npm |
npm-proxy |
registry.npmjs.org |
rubygems |
gem-proxy |
rubygems.org |
go |
go-proxy |
proxy.golang.org |
helm |
helm-bitnami |
charts.bitnami.com/bitnami |
yum |
yum-rocky, yum-fedora |
download.rockylinux.org, dl.fedoraproject.org |
raw |
raw-githubusercontent, raw-codeload-github, raw-packagist, raw-alpine |
raw.githubusercontent.com, codeload.github.com, repo.packagist.org, dl-cdn.alpinelinux.org |
Bootstrap is idempotent and runs from package.sh once the stack is healthy.
Package Cache Frontend 🔐
package-cache-frontend runs nginx:alpine and reverse-proxies upstream package-manager hostnames onto the matching Nexus repo path. Combined with extra_hosts DNS-hijack on consumers, package managers can hit their real upstream URL and still flow through the cache.
Two listener layers:
- HTTPS (port 443): per-hostname server certs signed by a dedicated CA. Used by the
infinitorunner (Ansible-drivenpip install,gem install,composer install,curl https://…). The runner trusts the CA via package-frontend-ca.sh. - HTTP (port 80): plain mirrors for
deb.debian.org,archive.ubuntu.com,security.ubuntu.com,dl-cdn.alpinelinux.org. Used by inner-dockerdDockerfile builds viabuild.extra_hostsDNS-hijack. No CA-trust required in the build container.
Cert generation runs in a throw-away alpine container driven by package-frontend-certs.sh before the frontend starts.
Activation 🎚️
The cache decision is exposed via Profile.registry_cache_active() in profile.py. When active:
- compose/cache.override.yml is layered on top of the base compose.yml by compose.py and down.py via common.py
compose_file_args. - The cache services are added.
- The runner's
infinitoservice receives:- bind-mounts for the registry-cache CA, the package-cache client snippets (
pip.conf,npmrc,apt.list), and the frontend CA file extra_hostsentries DNS-hijacking the HTTPS upstream hostnames to the frontend's static IPINFINITO_PACKAGE_CACHE_FRONTEND_IPenv var for the inner compose wrapper
- bind-mounts for the registry-cache CA, the package-cache client snippets (
- Cert generation, Nexus repo bootstrap, and runner trust-store install run from compose.py.
When inactive, the override is omitted: cache services do not exist, the runner has no cache mounts or DNS-hijack, package managers go direct to upstream.
CI signals (GITHUB_ACTIONS=true, RUNNING_ON_GITHUB=true, CI=true) deactivate. Fresh runner disks per CI job give no cross-run amortization.
Coverage Matrix 📋
| Traffic | Mechanism | Cached |
|---|---|---|
Image pulls (any registry, inner dockerd) |
registry-cache MITM via HTTP_PROXY env on dockerd |
✓ |
apt-get install (Ansible task in runner) |
apt.list URL-rewrite to package-cache:8081 |
✓ |
pip install (Ansible task in runner) |
pip.conf URL-rewrite |
✓ |
npm install (Ansible task in runner) |
.npmrc URL-rewrite |
✓ |
gem, composer, go, curl https://… (Ansible task in runner) |
DNS-hijack + frontend HTTPS + runner CA-trust | ✓ |
RUN apt-get install (inner Dockerfile build, Debian/Ubuntu) |
build.extra_hosts DNS-hijack + frontend HTTP |
✓ |
RUN apk add (inner Dockerfile build, Alpine ≤3.17 HTTP) |
build.extra_hosts DNS-hijack + frontend HTTP |
✓ |
RUN pip/npm/gem/composer/go install (inner Dockerfile build) |
none today | ✗ |
RUN apk add (Alpine ≥3.18 HTTPS) |
none today | ✗ |
| App container runtime traffic (HTTPS to external API) | none today | ✗ |
The HTTPS-only inner-build gap requires per-image CA-trust bootstrap, which is out of scope for the current minimal-invasive design.
Compose Wrapper Auto-Detection 🪄
The runner's compose wrapper at roles/sys-svc-compose/files/compose.py auto-detects compose files when invoked from per-app directories under /opt/compose/<app>/:
| File | When |
|---|---|
compose.yml |
always |
compose.override.yml |
when present (role provides it) |
compose.ca.override.yml |
when present (TLS self-signed CA-inject runs) |
compose.cache.override.yml |
generated on the fly when INFINITO_PACKAGE_CACHE_FRONTEND_IP is set; emits build.extra_hosts for every service that has a build: key |
pull.py delegates to the same wrapper so pull and build --pull operations see the identical -f set.
Environment Variables 🌳
The host-side env-vars live in scripts/meta/env/cache/registry.sh and scripts/meta/env/cache/package.sh. They are sourced via scripts/meta/env/all.sh, which BASH_ENV makes available to every Makefile recipe.
Per-variable defaults and purposes are in compose.yml.md under the cache section.
INFINITO_PACKAGE_CACHE_MAX_AGE_MIN (default 129600 = 90 days) controls how long every Nexus proxy repo holds an upstream response before revalidating. The bootstrap helper applies it as contentMaxAge, metadataMaxAge, and negativeCache.timeToLive. Lower it for fast-moving upstreams (apt-debian-security, branch-pinned tarballs) when staleness becomes visible.
Operations 🛠️
| Action | Command |
|---|---|
| Start the stack with caches | make up |
| Stop the stack | make down |
| Wipe local cache state | make cache-clean |
| Manually re-bootstrap Nexus repos | bash scripts/docker/cache/package.sh (after sourcing scripts/meta/env/cache/package.sh) |
| Manually regenerate frontend certs | bash scripts/docker/cache/package-frontend-certs.sh |
| Reload nginx in the frontend | docker exec infinito-package-cache-frontend nginx -s reload |
| Inspect cache hits | docker logs -f infinito-package-cache and docker logs -f infinito-package-cache-frontend |
Cache state persists under /var/cache/infinito/core/cache/. Paths are configurable via the env scripts above.
Adding a New Upstream ➕
When a new package manager or upstream needs caching:
- Register a Nexus proxy repo in package.sh.
- If the upstream uses HTTPS, add it to
HOSTNAMESin package-frontend-certs.sh so a leaf cert is issued. - Add a server-block in upstreams.conf that reverse-proxies onto the new Nexus repo path (rewrite if upstream URL prefix differs from the Nexus repo path).
- Add an
extra_hostsentry on theinfinitoservice in compose/cache.override.yml for runner-side traffic. - If the upstream is HTTP-only and inner-
dockerdbuilds need it, also add it to_CACHE_HTTP_HOSTNAMESin compose.py so the per-appcompose.cache.override.ymlincludes it inbuild.extra_hosts.
Background and Requirements 📚
- Compose-file env-var contract: compose.yml.md
- Original requirement and acceptance criteria: docs/requirements/012-package-cache-nexus3-oss.md
- Profile gating mechanics: profile.py