version: '3.8' services: # PostgreSQL database for production postgres: image: postgres:16-alpine container_name: placebo-postgres environment: POSTGRES_DB: placebo_backend_db POSTGRES_USER: placebo_user POSTGRES_PASSWORD: placebo_password volumes: - postgres_data:/var/lib/postgresql/data - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U placebo_user -d placebo_backend_db"] interval: 10s timeout: 5s retries: 5 networks: - placebo-network # Backend API (NestJS) backend: build: context: ./backend dockerfile: Dockerfile container_name: placebo-backend environment: NODE_ENV: production DATABASE_TYPE: postgres DATABASE_HOST: postgres DATABASE_PORT: 5432 DATABASE_USERNAME: placebo_user DATABASE_PASSWORD: placebo_password DATABASE_NAME: placebo_backend_db DATABASE_SYNCHRONIZE: "false" DATABASE_LOGGING: "false" CORS_ORIGIN: http://localhost:5173,http://localhost:3001 PORT: 3000 ports: - "3000:3000" depends_on: postgres: condition: service_healthy volumes: - ./backend/.env:/app/.env:ro - ./backend/uploads:/app/uploads healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => {if(r.statusCode !== 200) throw new Error()})"] interval: 30s timeout: 10s retries: 3 start_period: 40s networks: - placebo-network # Frontend (TanStack React) frontend: build: context: ./frontend dockerfile: Dockerfile container_name: placebo-frontend environment: NODE_ENV: production VITE_API_URL: http://localhost:3000 VITE_CMS_URL: http://localhost:1337 ports: - "3001:80" depends_on: - backend volumes: - ./frontend/.env:/app/.env:ro healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80/"] interval: 30s timeout: 10s retries: 3 start_period: 40s networks: - placebo-network # CMS (Strapi) cms: build: context: ./cms/cms dockerfile: Dockerfile container_name: placebo-cms environment: NODE_ENV: production DATABASE_CLIENT: postgres DATABASE_HOST: postgres DATABASE_PORT: 5432 DATABASE_NAME: placebo_db DATABASE_USERNAME: placebo_user DATABASE_PASSWORD: placebo_password DATABASE_SSL: "false" JWT_SECRET: ${JWT_SECRET:-your-jwt-secret-key-change-in-production} ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET:-your-admin-jwt-secret-key-change-in-production} APP_KEYS: ${APP_KEYS:-your-app-keys-change-in-production} API_TOKEN_SALT: ${API_TOKEN_SALT:-your-api-token-salt-change-in-production} TRANSFER_TOKEN_SALT: ${TRANSFER_TOKEN_SALT:-your-transfer-token-salt-change-in-production} CORS_ORIGIN: http://localhost:5173,http://localhost:3001 PORT: 1337 ports: - "1337:1337" depends_on: postgres: condition: service_healthy volumes: - ./cms/cms/.env:/app/.env:ro - ./cms/cms/public/uploads:/app/public/uploads - ./cms/cms/.tmp:/app/.tmp healthcheck: test: ["CMD", "node", "-e", "require('http').get('http://localhost:1337/_health', (r) => {if(r.statusCode !== 200) throw new Error()})"] interval: 30s timeout: 10s retries: 3 start_period: 60s networks: - placebo-network # Nginx reverse proxy (for production) nginx: image: nginx:alpine container_name: placebo-nginx ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/ssl:/etc/nginx/ssl:ro - ./frontend/dist:/usr/share/nginx/html:ro depends_on: - frontend - backend - cms networks: - placebo-network volumes: postgres_data: driver: local networks: placebo-network: driver: bridge