diff --git a/.env.template b/.env.template index 2f737dba..5c2b19c5 100644 --- a/.env.template +++ b/.env.template @@ -9,6 +9,9 @@ # ============================================================================= LOG_LEVEL=INFO PERFORMANCE_LOG_FORMAT=compact # compact|rich +# API server processes used by the Docker entrypoint (default: 1). +# Each process owns a separate pool when connection pooling is enabled. +# API_WORKERS=1 # SESSION_OBSERVERS_LIMIT=10 # GET_CONTEXT_MAX_TOKENS=100000 # MAX_FILE_SIZE=5242880 # Bytes diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index bc8e3f37..a9f6ea78 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -5,4 +5,4 @@ echo "Running database migrations..." /app/.venv/bin/python scripts/provision_db.py echo "Starting API server..." -exec /app/.venv/bin/fastapi run --host 0.0.0.0 src/main.py +exec /app/.venv/bin/fastapi run --host 0.0.0.0 --workers "${API_WORKERS:-1}" src/main.py diff --git a/docs/v3/contributing/self-hosting.mdx b/docs/v3/contributing/self-hosting.mdx index 02d361f1..4992530e 100644 --- a/docs/v3/contributing/self-hosting.mdx +++ b/docs/v3/contributing/self-hosting.mdx @@ -389,6 +389,20 @@ The default compose file is already production-oriented — ports bound to `127. - You can also run multiple deriver processes across machines — they coordinate via the database queue - Monitor deriver logs for processing backlog +### Scaling the API + +Set `API_WORKERS` to run multiple API server processes in the Docker container. It defaults to `1`, preserving the existing single-process behavior. + +When connection pooling is enabled (`DB_POOL_CLASS` is not `null`), each API process creates its own SQLAlchemy connection pool. Keep the combined capacity below the PostgreSQL connection limit: + +```text +API_WORKERS * (DB_POOL_SIZE + DB_MAX_OVERFLOW) < PostgreSQL max_connections +``` + +With the default pooled settings (`10 + 20`), each API worker can open up to 30 connections. For example, `API_WORKERS=3` allows up to 90 API connections. Leave additional headroom for the deriver, migrations, administration, and monitoring. + +When `DB_POOL_CLASS=null`, SQLAlchemy uses `NullPool`; `DB_POOL_SIZE` and `DB_MAX_OVERFLOW` do not apply, and connections are opened and closed per use. + ### Caching - The production compose enables Redis caching by default (`CACHE_ENABLED=true`) - For the development compose, enable manually: `CACHE_ENABLED=true`