compose/cache.shared.override.yml attaches every worktree stack's
infinito service to the primary's network (infinito-nexus-core_default,
172.30.0.0/24) without an ipv4_address. The default network declared
no ip_range, so Docker IPAM handed out the lowest free address and the
worktree stacks took 172.30.0.2, .3, .8, .9 and, whenever the primary
container was down, .10, the primary's INFINITO_IP4. The primary's
`make compose-up` then failed with "Address already in use", and with
it `make quality-high` on main.
The default network now declares ip_range: ${INFINITO_IP_RANGE}, the
upper half of INFINITO_SUBNET. Every static address of the stack (.1,
.4 to .6, .10, .53) sits in the lower half, so a container without a
static address can no longer take one.
INFINITO_IP_RANGE is derived from the final INFINITO_SUBNET after
custom.env is applied, because a worktree pins its subnet there and
`make dotenv-force` regenerates without it in the environment. Existing
worktrees therefore get their own slot's range without a new pin.
A lint checks that every static IP in default.env and every
172.30.${slot}.N pin in scripts/system/worktree/up.sh lies below the
pool.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
8.0 KiB
compose.yml 🐳
This page documents the rules that govern the top-level compose.yml and the environment variables it consumes. For general documentation rules (links, writing, RFC 2119 keywords), see documentation.md. For CI debugging workflows that use these variables, see ci.md.
Purpose 🎯
The top-level compose.yml is scoped to local development and CI testing only.
It MUST NOT be used to deploy or run a live Infinito.Nexus stack.
Production deployments MUST go through the Ansible roles and their own rendered compose files under roles/<role>/templates/.
Within its scope, compose.yml MUST stay a thin orchestration surface for the infinito runner container and its CoreDNS sidecar.
It MUST NOT host application roles, fixtures, or test harnesses.
Structure 📐
- You MUST consume tunable runtime parameters via strict substitution
${VAR:?Run 'make dotenv' to generate the .env single source of truth}; the default lives in default.env. Inline${VAR:-default}forms are forbidden and enforced by test_no_default_substitutions.py. - You MUST NOT hardcode values that differ between operators (paths, image tags, CPU or memory caps).
- You SHOULD annotate non-obvious keys with a short inline comment that explains why they exist, not what they do.
- You MUST document every new env var by adding the row below AND committing a one-line
# ...comment above its key in default.env.
Environment Variables 📋
All variables consumed by compose.yml. The per-variable default + comment lives next to the key in default.env; the tables below stay scoped to "what role does this variable play in compose.yml" and stop duplicating the default.
Image and Runtime
| Variable | Purpose |
|---|---|
INFINITO_IMAGE |
Image reference used by the infinito service. Composed at runtime by the env generator on GHA, empty locally so the build: block builds from source. |
INFINITO_PULL_POLICY |
Compose pull_policy. Defaults to never for local builds, generator overrides to always on GHA. |
INFINITO_CONTAINER |
Used directly as container_name. Derived from INFINITO_DISTRO by container.py. |
NIX_CONFIG |
Build-arg forwarded to the Dockerfile for Nix configuration. Pass-through if set in the calling shell. |
Resource Caps (OOM Reproduction)
These variables cap the infinito container so local runs can reproduce GitHub-hosted runner pressure.
Each default is 0, which Docker interprets as "unlimited" and is identical to omitting the key.
See ci.md for runner specs and reproduction profiles.
| Variable | Compose key | Example override |
|---|---|---|
INFINITO_MEM_LIMIT |
mem_limit |
16g |
INFINITO_MEMSWAP_LIMIT |
memswap_limit |
16g |
INFINITO_CPUS |
cpus |
4 |
You SHOULD set INFINITO_MEMSWAP_LIMIT to the same value as INFINITO_MEM_LIMIT to disable swap inflation.
Allowing swap masks OOM conditions that the real runner would hit.
Storage
| Variable | Purpose |
|---|---|
INFINITO_DOCKER_VOLUME |
Named volume (or host path) backing the nested Docker directory. Generator overrides to /mnt/docker on GHA. |
INFINITO_DOCKER_MOUNT |
Mount point inside the container for the nested Docker data. |
Caches
For activation, coverage, and operations of the registry-cache, package-cache, and package-cache-frontend services, see cache.md.
The env-var contracts each service expects strictly via ${VAR:?…} (consumed in compose.yml and compose/cache.override.yml):
| Variable | Purpose |
|---|---|
INFINITO_CACHE_REGISTRY_HOST_PATH |
Host path bind-mounted into registry-cache for blob/manifest persistence. |
INFINITO_CACHE_REGISTRY_CA_HOST_PATH |
Host path holding the proxy MITM CA bundle. Mounted writable into registry-cache, read-only into infinito. |
INFINITO_CACHE_REGISTRY_MAX_SIZE |
Maximum on-disk size. Computed by registry_cache_max_size.py as half the free disk at the cache path, floor 1g, fallback 2g. |
INFINITO_CACHE_REGISTRY_PROXY_CONF |
Bind source for the systemd drop-in inside infinito. Set by the dev tooling to the real proxy.conf under the cache profile. Driver-injected; not produced by make dotenv. |
INFINITO_CACHE_PACKAGE_HOST_PATH |
Host path bind-mounted at /nexus-data. |
INFINITO_CACHE_PACKAGE_HEAP |
JVM heap (-Xms/-Xmx). Computed as half free RAM, capped 2g, floor 1g. |
INFINITO_CACHE_PACKAGE_DIRECT_MEM |
MaxDirectMemorySize. Mirrors INFINITO_CACHE_PACKAGE_HEAP. |
INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX |
Soft quota for the default blobstore. Computed as half free disk at the cache path, floor 2g. |
INFINITO_CACHE_PACKAGE_MAX_AGE_MIN |
Cache freshness window in minutes, applied to every Nexus proxy repo (contentMaxAge/metadataMaxAge/negative-cache TTL). Stays strictly below the 7-day Valid-Until window Debian / Ubuntu generate in their apt Release files, so apt-get update never aborts with "Release file ... is expired". |
INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD |
Target value for the rotated Nexus admin password. Default is a stable per-host hash. |
INFINITO_CACHE_PACKAGE_PORT |
Host-side port mapped to Nexus REST/UI. Bound to ${INFINITO_BIND_IP} only. |
INFINITO_CACHE_PACKAGE_PIP_CONF |
Bind source for /etc/pip.conf. Driver-injected by make compose-up under the cache profile. |
INFINITO_CACHE_PACKAGE_NPMRC |
Bind source for /root/.npmrc. Driver-injected. |
INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR |
Host directory for the frontend CA. |
INFINITO_CACHE_PACKAGE_FRONTEND_CERTS_DIR |
Host directory for per-hostname leaf certs. |
INFINITO_CACHE_PACKAGE_FRONTEND_IP |
Static IPv4 address for the frontend on the compose default network. |
INFINITO_CACHE_PACKAGE_FRONTEND_CA_FILE |
Bind source for /opt/package-frontend-ca.crt inside the runner. Driver-injected. |
Networking
| Variable | Purpose |
|---|---|
INFINITO_DNS_IP |
Static IP assigned to the CoreDNS sidecar and used as the DNS server. Default lives in default.env. |
INFINITO_IP4 |
Static IPv4 address assigned to the infinito container. Default in default.env. |
INFINITO_DOMAIN |
Base domain exported into the container environment. Default in default.env. |
INFINITO_BIND_IP |
Host address that all published ports bind to. |
INFINITO_SUBNET |
IPAM subnet for the default bridge network. |
INFINITO_IP_RANGE |
IPAM pool for containers that join the default bridge without a static address, such as worktree stacks on the primary's cache network. It is the upper half of INFINITO_SUBNET, derived after custom.env by ip_range.py; every static address stays below it. |
INFINITO_GATEWAY |
IPAM gateway for the default bridge network. |
INFINITO_OUTER_NETWORK_MTU |
MTU for the bridge network. Lower when the host network is tunneled. |
Adding a Variable ➕
When you introduce a new env var in compose.yml, you MUST:
- Add the default + one-line
# ...comment to default.env; defaults never live incompose.yml. - Consume the variable strictly via
${VAR:?Run 'make dotenv' to generate the .env single source of truth}; bare${VAR}is acceptable only when the value is optional (e.g.NIX_CONFIG). - Add a row to the matching table above with its compose-side purpose.
- Cross-link the variable from the relevant workflow page when it exists only to drive a specific workflow (e.g. resource caps link to ci.md).