# Compose & Infrastructure Conventions This document outlines the standard conventions and best practices for Docker and Podman Compose files, volume layouts, networking, and smart image packaging across our infrastructure. --- ## 1. Storage & Host Volume Conventions - **Root Project Directory:** All project state lives under `/volume1/docker//` (e.g., `/volume1/docker/auth-yes/`, `/volume1/docker/spire/`). - **Standard Folder Naming:** Always use **`data`** for persistent application and database storage (never `db` or fragmented subfolders when a single root directory suffices). - **Local Bind Mount Syntax:** Always use the standard `driver_opts` local bind definition: ```yaml volumes: -data: driver: local driver_opts: type: none device: ${_DATA_PATH} o: bind ``` - **Zero-Trust Inter-Process Sockets:** - Sockets shared across stacks (e.g., the SPIFFE Workload API `agent.sock`) MUST use a **named volume** (`spire-socket`). - Application containers mount this volume read-only (`:ro`), ensuring zero access to sensitive server CA keys, SQLite databases, or internal host state. - Client stacks declare `spire-socket` as `external: true`. --- ## 2. Smart Container Images & Scripting Guidelines - **Pragmatic Scripting in Compose:** - Short one-line commands, flags, or glue checks in `command:` or `entrypoint:` are fine. - Complex configuration generation, heredoc templating (`cat << 'EOF'`), and heavy lifecycle bootstrapping MUST NOT be embedded in Compose files. - **Smart Image Packaging & Repository Segregation:** - Self-seeding configuration and container lifecycle scripts belong in the container image. - All smart image logic, Dockerfiles, entrypoint scripts (`entrypoint.sh`), and configuration templates (`templates/`) must be cleanly segregated in dedicated directories in the codebase (e.g., `spire/`). - **Persistence & User Modification:** - Smart image entrypoints must follow the self-seeding pattern: check if the target configuration exists on the host mount. - If missing on first boot $\rightarrow$ atomically write the master template with guiding comments. - If already present $\rightarrow$ leave it untouched, preserving all user modifications across restarts and image updates. --- ## 3. Container Registries, Mirrors & Image Distribution Our infrastructure uses a tiered registry architecture to balance local caching, private custom image hosting, and universal build portability: ### 3.1 Registry Tier Classification 1. **Primary Custom Image Registry (`${REG}` $\rightarrow$ `quay.atyg.org`):** - Hosts all internally built, project-specific custom images (`${REG}/library/:`). - Examples: `${REG}/library/auth-yes-api:latest`, `${REG}/library/spire-server:latest`, `${REG}/library/spire-agent:latest`. - Retention policy is set for **indefinite storage** (auto-pruning disabled). 2. **Docker Hub Pull-Through Cache (`acr.atyg.org`):** - Proxies and indefinitely caches upstream Docker Hub images on the local network. - Examples: `acr.atyg.org/library/postgres:18-alpine`, `acr.atyg.org/valkey/valkey:8-alpine`, `acr.atyg.org/library/alpine:3.20`. 3. **GHCR Pull-Through Mirror (`${GHCR_REG}` $\rightarrow$ `ghcr.atyg.org`):** - Proxies and caches GitHub Container Registry packages. - Examples: `${GHCR_REG}/spiffe/spire-server:1.9.3`, `${GHCR_REG}/spiffe/spire-agent:1.9.3`. ### 3.2 Universal Dockerfile Portability (Jules & External CI) - **Public Upstream Defaults:** Dockerfiles must declare public upstream registries by default so external agents (like Jules) and cloud CI runners can build without private `.atyg.org` DNS: ```dockerfile ARG SPIRE_UPSTREAM=ghcr.io/spiffe/spire-server:1.9.3 ARG BASE_IMAGE=alpine:3.20 FROM ${SPIRE_UPSTREAM} AS upstream FROM ${BASE_IMAGE} ``` - **Local Build Acceleration:** Local builds and CLI scripts (`infra/setup.ts`) can optionally pass `--build-arg` to pull through local mirrors (`ghcr.atyg.org`, `acr.atyg.org`). - **Push Destination:** Custom built images are tagged and pushed to the local authority `${REG}/library/...` (`quay.atyg.org`). ### 3.3 Compose Variable Cleanliness - **No Inline Defaults:** Always write clean variable references in Compose files (`image: ${REG}/library/spire-server:latest`, `image: ${GHCR_REG}/spiffe/spire-server:1.9.3`). - **Centralized Values:** Default registry variables belong strictly in `.env` and `infra/setup.ts`. --- ## 4. Networking & Traefik Routing - **Ingress Network:** - Web-facing containers connect to the pre-existing external network `traefik-net` (`external: true`). - **Network Routing Ambiguity Guard:** - Whenever a service connects to more than one network (e.g., internal bridge + `traefik-net`), always specify: ```yaml labels: - "traefik.docker.network=traefik-net" ``` - **Internal Service Mesh:** - Backends, databases, Valkey, and internal daemons communicate over a project bridge network (e.g., `auth-internal-net`) without exposing ports directly to the host. - **Standard Websecure Labels:** ```yaml labels: - "traefik.enable=true" - "traefik.docker.network=traefik-net" - "traefik.http.routers..rule=Host(`${SYSTEM_DOMAIN}`)" - "traefik.http.routers..entrypoints=websecure" - "traefik.http.routers..tls=true" - "traefik.http.services..loadbalancer.server.port=" ``` --- ## 5. Stack Independence & Decoupling - **Standalone Infrastructure Stacks:** - General-purpose infrastructure components (e.g., SPIRE, Auth-Yes, Registry Caches) are standalone stacks with their own dedicated project directories (`/volume1/docker/spire`, `/volume1/docker/auth-yes`). - Never nest one stack's storage or lifecycle under another stack's directory structure.