services: # PostgreSQL - Base de datos core (app) postgres-a76: image: postgres:18-alpine container_name: anexo76-postgres-a76 environment: POSTGRES_DB: anexo76_core POSTGRES_USER: postgres POSTGRES_PASSWORD: ${POSTGRES_APP_PASSWORD:-postgres} POSTGRES_INITDB_ARGS: "--encoding=UTF8" ports: - "5432:5432" volumes: - postgres_app_data:/var/lib/postgresql/data - ./scripts/postgres-app-entrypoint.sh:/docker-entrypoint-initdb.d/init-app.sh:ro networks: - backend-net restart: unless-stopped healthcheck: test: [ "CMD-SHELL", "pg_isready -U postgres -d anexo76_core || exit 1" ] interval: 5s timeout: 3s retries: 10 start_period: 20s logging: driver: "json-file" options: max-size: "10m" max-file: "3" deploy: resources: limits: memory: 512M reservations: memory: 256M shm_size: 128mb # Backend - FastAPI backend: build: context: ./backend dockerfile: Dockerfile args: - BUILDKIT_INLINE_CACHE=1 image: anexo76-backend:latest container_name: anexo76-backend environment: - DEBUG=${DEBUG:-True} - ENVIRONMENT=${ENVIRONMENT:-development} - PYTHONUNBUFFERED=1 - PYTHONDONTWRITEBYTECODE=1 - CORE_DB_HOST=${CORE_DB_HOST:-postgres-a76} - CORE_DB_PORT=${CORE_DB_PORT:-5432} - CORE_DB_NAME=${CORE_DB_NAME:-anexo76_core} - CORE_DB_USER=${CORE_DB_USER:-postgres} - CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres} # Keycloak — apunta al Keycloak del Hub (ya no tiene Keycloak propio) - CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:5173,http://localhost:3000} - SITAR_API_URL=${SITAR_API_URL} - SITAR_API_USER=${SITAR_API_USER} - SITAR_API_PASSWORD=${SITAR_API_PASSWORD} - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL:-""} - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-change-this-sync-token-in-production} - SPOKE_URLS=${SPOKE_URLS:-""} # Hub — URL interna para validación de licencias - HUB_URL=${HUB_URL:-http://host.docker.internal:8001} ports: - "8000:8000" depends_on: postgres-a76: condition: service_healthy volumes: - ./backend:/app - backend_cache:/app/__pycache__ - backend_uploads:/app/uploads - ./scripts/backend-entrypoint.sh:/entrypoint.sh:ro networks: - backend-net - frontend-net restart: unless-stopped entrypoint: [ "/entrypoint.sh" ] command: [ "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload", "--log-level", "info" ] healthcheck: test: [ "CMD-SHELL", "curl -f http://localhost:8000/api/health || exit 1" ] interval: 15s timeout: 5s retries: 5 start_period: 60s logging: driver: "json-file" options: max-size: "10m" max-file: "3" deploy: resources: limits: memory: 512M reservations: memory: 256M # Frontend - SvelteKit frontend: build: context: ./frontend dockerfile: Dockerfile args: - BUILDKIT_INLINE_CACHE=1 image: anexo76-frontend:latest container_name: anexo76-frontend environment: - NODE_ENV=${NODE_ENV:-development} - VITE_API_URL=${VITE_API_URL:-http://localhost:8000/api/} - INTERNAL_API_URL=${INTERNAL_API_URL:-http://backend:8000/api/} - VITE_KEYCLOAK_URL=${VITE_KEYCLOAK_URL:-http://localhost:8085/kcauth} - VITE_KEYCLOAK_REALM=${VITE_KEYCLOAK_REALM:-master} - VITE_KEYCLOAK_CLIENT_ID=${VITE_KEYCLOAK_CLIENT_ID:-anexo76-frontend} # Hub — URL pública para el browser y URL interna para server-side - VITE_HUB_URL=${VITE_HUB_URL:-http://localhost:8001} - INTERNAL_HUB_URL=${INTERNAL_HUB_URL:-http://host.docker.internal:8001} # CORS / CSRF — trusted origins para svelte.config.js - CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:5173,http://localhost:3001} - TRUSTED_ORIGINS=${TRUSTED_ORIGINS:-} ports: - "5173:5173" depends_on: backend: condition: service_healthy entrypoint: [ "/frontend-entrypoint.sh" ] volumes: - ./frontend:/app - frontend_node_modules:/app/node_modules - ./scripts/frontend-entrypoint.sh:/frontend-entrypoint.sh:ro networks: - frontend-net restart: unless-stopped command: [ "pnpm", "run", "dev", "--", "--host", "0.0.0.0" ] healthcheck: test: [ "CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:5173/ || exit 1" ] interval: 15s timeout: 5s retries: 5 start_period: 45s logging: driver: "json-file" options: max-size: "10m" max-file: "3" # Celery Worker celery_worker: build: ./backend container_name: worker command: celery -A core.celery_app worker --loglevel=info environment: - DEBUG=${DEBUG:-True} - ENVIRONMENT=${ENVIRONMENT:-development} - PYTHONUNBUFFERED=1 - PYTHONDONTWRITEBYTECODE=1 - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL:-""} - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-change-this-sync-token-in-production} - SPOKE_URLS=${SPOKE_URLS:-""} - CORE_DB_HOST=${CORE_DB_HOST:-postgres-a76} - CORE_DB_PORT=${CORE_DB_PORT:-5432} - CORE_DB_NAME=${CORE_DB_NAME:-anexo76_core} - CORE_DB_USER=${CORE_DB_USER:-postgres} - CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres} - VALKEY_URL=redis://valkey:6379/0 - SITAR_API_URL=${SITAR_API_URL} - SITAR_API_USER=${SITAR_API_USER} - SITAR_API_PASSWORD=${SITAR_API_PASSWORD} depends_on: - backend - valkey volumes: - ./backend:/app - backend_cache:/app/__pycache__ - backend_uploads:/app/uploads networks: - backend-net # Celery Beat celery_beat: build: ./backend container_name: celery_beat command: celery -A core.celery_app beat --loglevel=info environment: - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379/0} - CENTRAL_SERVER_URL=${CENTRAL_SERVER_URL:-""} - SYNC_SECRET_TOKEN=${SYNC_SECRET_TOKEN:-change-this-sync-token-in-production} - SPOKE_URLS=${SPOKE_URLS:-""} - CORE_DB_HOST=${CORE_DB_HOST:-postgres-a76} - CORE_DB_PORT=${CORE_DB_PORT:-5432} - CORE_DB_NAME=${CORE_DB_NAME:-anexo76_core} - CORE_DB_USER=${CORE_DB_USER:-postgres} - CORE_DB_PASSWORD=${POSTGRES_APP_PASSWORD:-postgres} depends_on: - backend - valkey volumes: - ./backend:/app - backend_cache:/app/__pycache__ - backend_uploads:/app/uploads networks: - backend-net valkey: image: valkey/valkey:7.2 container_name: valkey restart: always ports: - "6379:6379" networks: - backend-net volumes: postgres_app_data: driver: local frontend_node_modules: driver: local backend_cache: driver: local backend_uploads: driver: local networks: backend-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16 frontend-net: driver: bridge ipam: config: - subnet: 172.22.0.0/16