chore(canary): merge backend compose into project root

Single project-root compose.yml + dev.compose.yml manage all services.

Production stack (compose.yml):
- nginx          public ingress on \${NGINX_HOST_PORT:-22784}:80
- canary         Go backend, NO host port (reachable only via nginx)
                 distroless/static:nonroot Dockerfile
                 healthcheck → /healthz, depends on postgres + redis
- postgres       postgres:18-alpine, no host port, named volume pgdata
- redis          redis:7-alpine, no host port, named volume redisdata
- (geolite vol)  read-only mount /data for GeoLite2-City.mmdb

Dev stack (dev.compose.yml):
- nginx          dev ingress on \${NGINX_HOST_PORT:-58495}:80
- frontend       Vite HMR on \${FRONTEND_HOST_PORT:-15723}:5173
- canary         Air hot-reload (canary.dev Dockerfile, bind-mounts ./backend)
                 OTel exports to jaeger:4317
- postgres       host port \${POSTGRES_DEV_PORT:-5447}:5432 for psql access
- redis          host port \${REDIS_DEV_PORT:-6022}:6379 for redis-cli access
- jaeger         UI 16686, OTLP gRPC 4317, OTLP HTTP 4318
- volumes        gocache + gomodcache speed up rebuilds

All randomized host ports preserved exactly:
  22784 (prod nginx), 58495 (dev nginx), 15723 (vite), 5447 (pg dev),
  6022 (redis dev), 16686/4317/4318 (jaeger).

Backend has no host port in production: nginx is the single entrypoint.
Cloudflare Tunnel sidecar terminates at nginx:80 via cloudflared.compose.yml.

backend/compose.yml + backend/dev.compose.yml deleted (merged here).

Validation:
- docker compose -f compose.yml config           — OK
- docker compose -f dev.compose.yml config       — OK
- docker compose -f compose.yml -f cloudflared.compose.yml config — OK
This commit is contained in:
CarterPerez-dev 2026-05-10 05:23:55 -04:00
parent 7fa2861e7a
commit 8b70bfbba9
4 changed files with 196 additions and 109 deletions

View File

@ -1,59 +0,0 @@
# AngelaMos | 2026
# compose.yml - Production compose
services:
api:
image: go-backend:latest
ports:
- "8085:8080"
environment:
- ENVIRONMENT=production
- DATABASE_URL=postgres://postgres:postgres@postgres:5432/app?sslmode=disable
- REDIS_URL=redis://redis:6379/0
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
deploy:
resources:
limits:
memory: 256M
reservations:
memory: 128M
postgres:
image: postgres:18-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
volumes:
pgdata:
redisdata:

View File

@ -1,45 +0,0 @@
# AngelaMos | 2026
# dev.compose.yml - Development compose
services:
postgres:
image: postgres:18-alpine
ports:
- "5447:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app
volumes:
- pgdata_dev:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6022:6379"
command: redis-server --appendonly yes
volumes:
- redisdata_dev:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
jaeger:
image: jaegertracing/all-in-one:1.54
ports:
- "16686:16686"
- "4317:4317"
- "4318:4318"
environment:
COLLECTOR_OTLP_ENABLED: "true"
volumes:
pgdata_dev:
redisdata_dev:

View File

@ -1,9 +1,10 @@
# ============================================================================= # =============================================================================
# AngelaMos | 2026 # ©AngelaMos | 2026
# compose.yml # compose.yml
# ============================================================================= # =============================================================================
# Production compose - Nginx serving built React app # Production compose: nginx → canary backend → postgres + redis
# For Cloudflare tunnel: docker compose -f compose.yml -f cloudflared.compose.yml up # Cloudflare tunnel overlay:
# docker compose -f compose.yml -f cloudflared.compose.yml up -d
# ============================================================================= # =============================================================================
name: ${APP_NAME:-canary-token-generator} name: ${APP_NAME:-canary-token-generator}
@ -16,11 +17,15 @@ services:
args: args:
- VITE_API_URL=${VITE_API_URL:-/api} - VITE_API_URL=${VITE_API_URL:-/api}
- VITE_APP_TITLE=${VITE_APP_TITLE:-Canary Token Generator} - VITE_APP_TITLE=${VITE_APP_TITLE:-Canary Token Generator}
- VITE_TURNSTILE_SITE_KEY=${VITE_TURNSTILE_SITE_KEY:-}
container_name: ${APP_NAME:-canary-token-generator}-nginx container_name: ${APP_NAME:-canary-token-generator}-nginx
ports: ports:
- "${NGINX_HOST_PORT:-22784}:80" - "${NGINX_HOST_PORT:-22784}:80"
networks: networks:
- app - app
depends_on:
canary:
condition: service_healthy
deploy: deploy:
resources: resources:
limits: limits:
@ -31,6 +36,96 @@ services:
memory: 64M memory: 64M
restart: unless-stopped restart: unless-stopped
canary:
build:
context: ./backend
dockerfile: ../infra/docker/canary.prod
container_name: ${APP_NAME:-canary-token-generator}-canary
environment:
- ENVIRONMENT=production
- DATABASE_URL=postgres://canary:${POSTGRES_PASSWORD}@postgres:5432/canary?sslmode=disable
- REDIS_URL=redis://redis:6379/0
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL}
- TURNSTILE_SECRET=${TURNSTILE_SECRET}
- OPERATOR_TOKEN=${OPERATOR_TOKEN}
- GEOLITE_PATH=/data/GeoLite2-City.mmdb
- WEBHOOK_HMAC_SECRET=${WEBHOOK_HMAC_SECRET:-}
- MYSQL_FAKE_ENABLED=${MYSQL_FAKE_ENABLED:-false}
- LOG_LEVEL=${LOG_LEVEL:-info}
- LOG_FORMAT=${LOG_FORMAT:-json}
- OTEL_ENABLED=${OTEL_ENABLED:-false}
- OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT:-}
volumes:
- geolite:/data:ro
networks:
- app
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
deploy:
resources:
limits:
cpus: '1.0'
memory: 256M
reservations:
cpus: '0.25'
memory: 64M
restart: unless-stopped
postgres:
image: postgres:18-alpine
container_name: ${APP_NAME:-canary-token-generator}-postgres
environment:
POSTGRES_USER: canary
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: canary
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U canary -d canary"]
interval: 5s
timeout: 5s
retries: 5
deploy:
resources:
limits:
memory: 512M
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: ${APP_NAME:-canary-token-generator}-redis
command: redis-server --appendonly yes
volumes:
- redisdata:/data
networks:
- app
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
deploy:
resources:
limits:
memory: 128M
restart: unless-stopped
networks: networks:
app: app:
driver: bridge driver: bridge
volumes:
pgdata:
redisdata:
geolite:

View File

@ -1,8 +1,8 @@
# ============================================================================= # =============================================================================
# AngelaMos | 2026 # ©AngelaMos | 2026
# dev.compose.yml # dev.compose.yml
# ============================================================================= # =============================================================================
# Development compose - Nginx + Vite dev server with HMR # Development compose: nginx → vite (HMR) + canary (Air hot-reload) + Postgres + Redis + Jaeger
# Uses .env.development # Uses .env.development
# ============================================================================= # =============================================================================
@ -20,6 +20,8 @@ services:
depends_on: depends_on:
frontend: frontend:
condition: service_started condition: service_started
canary:
condition: service_healthy
networks: networks:
- app - app
restart: unless-stopped restart: unless-stopped
@ -37,6 +39,96 @@ services:
environment: environment:
- VITE_API_URL=${VITE_API_URL:-/api} - VITE_API_URL=${VITE_API_URL:-/api}
- VITE_APP_TITLE=${VITE_APP_TITLE:-Canary Token Generator} - VITE_APP_TITLE=${VITE_APP_TITLE:-Canary Token Generator}
- VITE_TURNSTILE_SITE_KEY=${VITE_TURNSTILE_SITE_KEY:-}
networks:
- app
restart: unless-stopped
canary:
build:
context: ./backend
dockerfile: ../infra/docker/canary.dev
container_name: ${APP_NAME:-canary-token-generator}-canary-dev
volumes:
- ./backend:/app
- canary_gocache:/root/.cache/go-build
- canary_gomodcache:/go/pkg/mod
environment:
- ENVIRONMENT=development
- DATABASE_URL=postgres://canary:canary@postgres:5432/canary?sslmode=disable
- REDIS_URL=redis://redis:6379/0
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL:-http://localhost:58495}
- TURNSTILE_SECRET=${TURNSTILE_SECRET:-}
- OPERATOR_TOKEN=${OPERATOR_TOKEN:-dev-operator-token}
- GEOLITE_PATH=${GEOLITE_PATH:-/app/testdata/GeoLite2-City.mmdb}
- WEBHOOK_HMAC_SECRET=${WEBHOOK_HMAC_SECRET:-}
- MYSQL_FAKE_ENABLED=${MYSQL_FAKE_ENABLED:-false}
- LOG_LEVEL=debug
- LOG_FORMAT=text
- OTEL_ENABLED=${OTEL_ENABLED:-true}
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317
- OTEL_INSECURE=true
networks:
- app
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
restart: unless-stopped
postgres:
image: postgres:18-alpine
container_name: ${APP_NAME:-canary-token-generator}-postgres-dev
ports:
- "${POSTGRES_DEV_PORT:-5447}:5432"
environment:
POSTGRES_USER: canary
POSTGRES_PASSWORD: canary
POSTGRES_DB: canary
volumes:
- pgdata_dev:/var/lib/postgresql/data
networks:
- app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U canary -d canary"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: ${APP_NAME:-canary-token-generator}-redis-dev
ports:
- "${REDIS_DEV_PORT:-6022}:6379"
command: redis-server --appendonly yes
volumes:
- redisdata_dev:/data
networks:
- app
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
jaeger:
image: jaegertracing/all-in-one:1.54
container_name: ${APP_NAME:-canary-token-generator}-jaeger-dev
ports:
- "${JAEGER_UI_PORT:-16686}:16686"
- "${JAEGER_OTLP_GRPC_PORT:-4317}:4317"
- "${JAEGER_OTLP_HTTP_PORT:-4318}:4318"
environment:
COLLECTOR_OTLP_ENABLED: "true"
networks: networks:
- app - app
restart: unless-stopped restart: unless-stopped
@ -47,3 +139,7 @@ networks:
volumes: volumes:
frontend_modules: frontend_modules:
pgdata_dev:
redisdata_dev:
canary_gocache:
canary_gomodcache: