diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..2a33c1d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,101 @@ +# Production overlay for LegacyHUB. +# +# Usage: +# cp .env.prod.example .env.prod +# $EDITOR .env.prod # rotate every credential +# docker compose \ +# -f docker-compose.yml -f docker-compose.prod.yml \ +# --env-file .env.prod \ +# up -d --build --force-recreate +# +# This overlay narrows the dev-friendly defaults: +# - removes published ports from data services (only api stays public); +# - turns on the OpenSearch security plugin and forces an admin password; +# - requires CORS_ALLOWED_ORIGINS to be set (no localhost fallback); +# - bumps Java + worker concurrency for real workloads; +# - drops the MinIO console. + +services: + postgres: + ports: !reset [] + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set} + restart: always + + minio: + command: server /data + ports: !reset [] + environment: + MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:?MINIO_ACCESS_KEY must be set} + MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:?MINIO_SECRET_KEY must be set} + restart: always + + opensearch: + ports: !reset [] + environment: + - discovery.type=single-node + - bootstrap.memory_lock=true + - "OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g" + - DISABLE_INSTALL_DEMO_CONFIG=true + - plugins.security.disabled=false + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_ADMIN_PASSWORD:?OPENSEARCH_ADMIN_PASSWORD must be set} + restart: always + + qdrant: + ports: !reset [] + environment: + QDRANT__SERVICE__API_KEY: ${QDRANT_API_KEY:?QDRANT_API_KEY must be set} + restart: always + + redis: + ports: !reset [] + restart: always + + api: + environment: + <<: &prod-env + POSTGRES_HOST: ${POSTGRES_HOST} + POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + MINIO_ENDPOINT: ${MINIO_ENDPOINT} + MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY} + MINIO_SECRET_KEY: ${MINIO_SECRET_KEY} + MINIO_BUCKET_ORIGINALS: ${MINIO_BUCKET_ORIGINALS} + MINIO_BUCKET_DERIVED: ${MINIO_BUCKET_DERIVED} + MINIO_SECURE: "true" + OPENSEARCH_HOST: ${OPENSEARCH_HOST} + OPENSEARCH_PORT: ${OPENSEARCH_PORT} + OPENSEARCH_USE_SSL: "true" + OPENSEARCH_VERIFY_CERTS: "true" + OPENSEARCH_USER: ${OPENSEARCH_USER} + OPENSEARCH_PASSWORD: ${OPENSEARCH_PASSWORD} + OPENSEARCH_INDEX_CHUNKS: ${OPENSEARCH_INDEX_CHUNKS} + QDRANT_HOST: ${QDRANT_HOST} + QDRANT_PORT: ${QDRANT_PORT} + QDRANT_API_KEY: ${QDRANT_API_KEY} + QDRANT_COLLECTION_CHUNKS: ${QDRANT_COLLECTION_CHUNKS} + REDIS_URL: ${REDIS_URL} + OCR_LANGUAGES: ${OCR_LANGUAGES} + OCR_ENABLED: ${OCR_ENABLED} + DOCLING_OCR_ENABLED: ${DOCLING_OCR_ENABLED} + MAX_DOCUMENT_TIMEOUT_SECONDS: ${MAX_DOCUMENT_TIMEOUT_SECONDS} + EMBEDDING_MODEL: ${EMBEDDING_MODEL} + EMBEDDING_DEVICE: ${EMBEDDING_DEVICE} + RERANKER_MODEL: ${RERANKER_MODEL} + RERANKER_DEVICE: ${RERANKER_DEVICE} + RERANKER_ENABLED: ${RERANKER_ENABLED} + APP_LOG_LEVEL: ${APP_LOG_LEVEL} + APP_INPUT_DIR: /data/input + APP_WORK_DIR: /data/work + CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:?CORS_ALLOWED_ORIGINS must be set (no * in production)} + restart: always + + worker: + command: ["celery", "-A", "app.workers.celery_app", "worker", "--loglevel=INFO", "--concurrency=4"] + environment: + <<: *prod-env + restart: always