Two failures from the first real deploy, both repo-side: 1. The container logged "exec /usr/bin/resume-server: no such file or directory" and restarted until the health gate failed. The build host has cgo enabled, so the binary carried the nix glibc interpreter, and the distroless/static image has no dynamic loader. Every SQLite path already uses pure-Go modernc.org/sqlite, so build with CGO_ENABLED=0 and ship a static binary. 2. Compose warned about orphan containers ai-resume-backend-1 and ai-resume-db-1: /home/sam/deployment/ai-resume on .13 already owns the `ai-resume` compose project (on the ai-mesh network). Sharing the name makes each stack visible as orphans to the other, so a `compose down --remove-orphans` in either could stop the other. Run the site as project `ai-resume-site`; the image tag stays ai-resume:local and the port binding, mem_limit and memswap_limit are untouched.
70 lines
2.6 KiB
YAML
70 lines
2.6 KiB
YAML
# Container shape for the Mode A site (docs/phase-2-static-site.md §2.1).
|
|
#
|
|
# Values are fixed by the specification and must not drift:
|
|
# mem_limit == memswap_limit == 120m so the app cannot eat the host's swap
|
|
# ports published on 127.0.0.1 only Caddy on .35 is the only public listener
|
|
# logging capped one bad day cannot fill the disk
|
|
#
|
|
# Run it (build the artefacts first — Phase 3's deploy.sh does both):
|
|
#
|
|
# make build dist
|
|
# docker compose -f deploy/compose.yml up -d
|
|
#
|
|
# `curl http://127.0.0.1:8096/healthz` must answer with static ready.
|
|
|
|
# The compose project name is deliberately not `ai-resume`: /home/sam/deployment/
|
|
# ai-resume on .13 already runs a compose project by that name (backend + db on
|
|
# the ai-mesh network). Sharing the name would make this site's containers show
|
|
# up as orphans in that project and vice versa, so anything running `compose
|
|
# down --remove-orphans` under either project could stop the other. The image
|
|
# tag stays ai-resume:local (Phase 3 fixes it); only the project namespace is
|
|
# distinct.
|
|
name: ai-resume-site
|
|
|
|
services:
|
|
resume:
|
|
image: ai-resume:local
|
|
build:
|
|
context: ..
|
|
dockerfile: deploy/Dockerfile
|
|
restart: unless-stopped
|
|
mem_limit: 120m
|
|
memswap_limit: 120m
|
|
# The app listens on 0.0.0.0 inside the container namespace; the host-side
|
|
# binding is loopback, so nothing is reachable from the LAN except through
|
|
# docker-proxy on 127.0.0.1:8096 — which is what Caddy proxies to.
|
|
ports:
|
|
- "127.0.0.1:8096:8096"
|
|
read_only: true
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: 10m
|
|
max-file: "3"
|
|
healthcheck:
|
|
test: ["CMD", "/usr/bin/resume-server", "--health-check"]
|
|
interval: 30s
|
|
timeout: 3s
|
|
retries: 3
|
|
start_period: 5s
|
|
|
|
# The project network gets an explicit, small subnet. The host's docker daemon
|
|
# has every default address pool allocated by other projects ("all predefined
|
|
# address pools have been fully subnetted"), so an implicit compose network
|
|
# cannot be created. Pinning /28 of RFC1918 space that no other bridge or route
|
|
# on .13 uses keeps this deploy independent of the host's pool exhaustion and
|
|
# does not touch any other container. Single-service stack: two addresses is
|
|
# plenty.
|
|
networks:
|
|
default:
|
|
ipam:
|
|
config:
|
|
- subnet: 10.89.0.0/28
|
|
gateway: 10.89.0.1
|
|
|
|
# Phase 4/5 add at most three more containers (Qdrant, Prefect worker, query
|
|
# service) inside the 4-container and 1.0 GiB budget in AGENTS.md §4.3. Mode A
|
|
# needs none of them and must keep working when they are stopped (test A1).
|