# ============================================================================= # feature-container-runtime-contract — base docker-compose service definition # # Usage: # docker compose up # base only # docker compose -f docker-compose.yml -f docker-compose.dev.yml up # dev override # docker compose -f docker-compose.yml -f docker-compose.local.yml up # local override # # Graceful-shutdown sync table (feature-container-runtime-contract D5): # App shutdown timeout : 30s (APP_SERVER_SHUTDOWN_TIMEOUT — SSOT: docs/registries/ # env-keys.yaml, owner feature-env-driven-runtime-configuration) # Container preStop : 5s (handled by the orchestrator / stop_grace_period offset) # stop_grace_period : 40s (30s app drain + 5s preStop + 5s safety margin) # # Ownership: env-driven-config owns the app shutdown *value* (30s); this contract owns the # *relationship* (stop_grace_period >= app timeout + preStop + margin). The grace is therefore # sized UP to the env-keys SSOT value (30s) — you do not shrink the app drain window to fit an # arbitrary grace. This resolves the earlier 20s/35s drift, which had assumed an app-timeout # value this branch does not own (env-keys.yaml is the SSOT and sets 30s). # # Memory limit (D4): # The container memory limit MUST be set for -XX:MaxRAMPercentage=75 to be meaningful. # Default 512m suits a small service; adjust per deployment. # ============================================================================= services: app: build: context: src/ dockerfile: Dockerfile args: RELEASE_VERSION: "${RELEASE_VERSION:-0.0.1}" BUILD_VERSION: "${BUILD_VERSION:-0.0.1+0000000}" GIT_SHA: "${GIT_SHA:-0000000}" SOURCE_URL: "${SOURCE_URL:-https://example.invalid/ca-tmpl}" image: caskeleton:${BUILD_VERSION:-0.0.1_local_0000000} ports: - "${APP_SERVER_PORT:-8080}:8080" - "9001:9001" environment: TZ: "UTC" LANG: "C.UTF-8" LC_ALL: "C.UTF-8" # Graceful shutdown — fallback matches the env-keys.yaml SSOT default (30s). # Override via .env or docker compose --env-file if you need a different value. APP_SERVER_SHUTDOWN_TIMEOUT: "${APP_SERVER_SHUTDOWN_TIMEOUT:-30s}" APP_SERVER_SHUTDOWN: "graceful" # ---- Read-only root filesystem (D2) ------------------------------------- # The application must write only to the tmpfs mounts declared below. # /tmp — Tomcat basedir (see -Dserver.tomcat.basedir=/tmp in Dockerfile). # /var/tmp/heap — heap dump landing zone (see -XX:HeapDumpPath=/var/tmp/heap). read_only: true tmpfs: - /tmp:mode=1777,size=128m - /var/tmp/heap:mode=1777,size=512m # ---- Fileserver storage volume ------------------------------------------ # A named volume, not a tmpfs and not the read-only root. The Fileserver platform's default # storage root is /var/lib/backend/files, and with a read-only root and no mount there was # nowhere on the image it could legally write: enabling the capability failed on its first # upload rather than at startup. The volume is declared unconditionally because a volume # nobody writes to costs nothing, while a missing one costs an outage. # # Ownership: the image runs as uid/gid 1000 (see src/Dockerfile). Docker initialises a fresh # named volume from the image path's ownership, so the directory is created in the image with # that owner; a pre-existing volume or a host bind mount must be chowned to 1000:1000 by the # operator, or every write is refused with a permission error the application reports as # STORAGE_UNAVAILABLE. volumes: - fileserver-data:/var/lib/backend/files # ---- Memory limit (D4) -------------------------------------------------- # Must be set so -XX:MaxRAMPercentage=75 can compute a meaningful heap bound. mem_limit: 512m memswap_limit: 512m # ---- Graceful shutdown (D5) --------------------------------------------- # stop_grace_period = app drain (30s, env-keys SSOT) + preStop (5s) + margin (5s) = 40s. stop_grace_period: 40s # ---- Health check ------------------------------------------------------- # Targets the actuator readiness probe on the management port (9001). # CROSS-FEATURE COUPLING: /actuator/health/readiness is implemented by the # parallel runtime-health + actuator branches. This healthcheck will report # UNHEALTHY in this worktree until those branches are merged. healthcheck: test: - "CMD" - "wget" - "--no-verbose" - "--tries=1" - "--spider" - "http://localhost:9001/actuator/health/readiness" interval: 30s timeout: 5s start_period: 60s retries: 3 restart: unless-stopped volumes: # Survives container replacement, which is the point: published content outlives the process # that wrote it. Back this with real storage in any deployment that keeps files. fileserver-data: