Files
goodgo-platform/docker-compose.prod.yml
Ho Ngoc Hai 7c9f682046 feat(deploy): add production Dockerfiles and CI/CD pipeline
- Multi-stage Dockerfile for apps/api (NestJS) and apps/web (Next.js standalone)
- Production docker-compose.prod.yml with all services, health checks, and security
- Real deploy.yml pipeline: build → push to GHCR → deploy staging/production
- .dockerignore for optimized build context
- Enable Next.js standalone output mode

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 04:03:27 +07:00

213 lines
5.7 KiB
YAML

services:
api:
image: ${REGISTRY_URL:-ghcr.io/goodgo}/goodgo-api:${IMAGE_TAG:-latest}
container_name: goodgo-api
restart: unless-stopped
ports:
- '${API_PORT:-3001}:3001'
environment:
NODE_ENV: production
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
REDIS_URL: redis://redis:6379
TYPESENSE_HOST: typesense
TYPESENSE_PORT: 8108
TYPESENSE_API_KEY: ${TYPESENSE_API_KEY}
JWT_SECRET: ${JWT_SECRET}
MINIO_ENDPOINT: minio
MINIO_PORT: 9000
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
AI_SERVICES_URL: http://ai-services:8000
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
typesense:
condition: service_healthy
healthcheck:
test: ['CMD', 'node', '-e', "fetch('http://localhost:3001/health').then(r => { if (!r.ok) throw 1 })"]
interval: 30s
timeout: 5s
retries: 5
start_period: 30s
networks:
- goodgo-net
web:
image: ${REGISTRY_URL:-ghcr.io/goodgo}/goodgo-web:${IMAGE_TAG:-latest}
container_name: goodgo-web
restart: unless-stopped
ports:
- '${WEB_PORT:-3000}:3000'
environment:
NODE_ENV: production
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://api:3001}
depends_on:
api:
condition: service_healthy
healthcheck:
test: ['CMD', 'node', '-e', "fetch('http://localhost:3000').then(r => { if (!r.ok) throw 1 }).catch(() => process.exit(1))"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
networks:
- goodgo-net
postgres:
image: postgis/postgis:16-3.4
container_name: goodgo-postgres
restart: unless-stopped
ports:
- '${DB_PORT:-5432}:5432'
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${DB_USER} -d ${DB_NAME}']
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- goodgo-net
redis:
image: redis:7-alpine
container_name: goodgo-redis
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} --maxmemory 512mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
healthcheck:
test: ['CMD', 'redis-cli', '-a', '${REDIS_PASSWORD}', 'ping']
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
networks:
- goodgo-net
typesense:
image: typesense/typesense:27.1
container_name: goodgo-typesense
restart: unless-stopped
environment:
TYPESENSE_API_KEY: ${TYPESENSE_API_KEY}
TYPESENSE_DATA_DIR: /data
volumes:
- typesense_data:/data
healthcheck:
test: ['CMD', 'curl', '-sf', 'http://localhost:8108/health']
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
networks:
- goodgo-net
minio:
image: minio/minio:latest
container_name: goodgo-minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
volumes:
- minio_data:/data
healthcheck:
test: ['CMD', 'mc', 'ready', 'local']
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
networks:
- goodgo-net
ai-services:
image: ${REGISTRY_URL:-ghcr.io/goodgo}/goodgo-ai-services:${IMAGE_TAG:-latest}
container_name: goodgo-ai-services
restart: unless-stopped
environment:
AI_DEBUG: 'false'
AI_LOG_LEVEL: info
healthcheck:
test: ['CMD', 'python', '-c', 'import httpx; httpx.get("http://localhost:8000/health").raise_for_status()']
interval: 30s
timeout: 5s
retries: 5
start_period: 30s
networks:
- goodgo-net
prometheus:
image: prom/prometheus:v2.51.0
container_name: goodgo-prometheus
restart: unless-stopped
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
volumes:
- ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:9090/-/healthy']
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
networks:
- goodgo-net
grafana:
image: grafana/grafana:10.4.1
container_name: goodgo-grafana
restart: unless-stopped
ports:
- '${GRAFANA_PORT:-3002}:3000'
environment:
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER}
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
GF_USERS_ALLOW_SIGN_UP: 'false'
GF_SERVER_ROOT_URL: ${GRAFANA_ROOT_URL:-http://localhost:3002}
volumes:
- ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro
- ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro
- grafana_data:/var/lib/grafana
depends_on:
prometheus:
condition: service_healthy
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:3000/api/health']
interval: 15s
timeout: 5s
retries: 3
start_period: 15s
networks:
- goodgo-net
volumes:
pgdata:
driver: local
redis_data:
driver: local
typesense_data:
driver: local
minio_data:
driver: local
prometheus_data:
driver: local
grafana_data:
driver: local
networks:
goodgo-net:
driver: bridge
name: goodgo-net