<@ULVA73B9P> I want to use connection pooling in m...
# ask-marvin
j
@Marvin I want to use connection pooling in my prefect server, does it work ok even running multiple instances? This is self-hosted Prefect 3.6.5
m
thought for 263.5 seconds
Short answer: yes—connection pooling works well with multiple Prefect API instances. In Prefect 3.6.5 the API uses SQLAlchemy’s async engine with a per-process connection pool, so you can safely run multiple API instances behind a load balancer. Just plan your database capacity accordingly. Key points - Pools are per-process (and per event loop). If you run N API instances (and/or multiple worker processes per instance), total potential DB connections scale with that. - Rough capacity planning: - Min connections ≈ instances × workers × pool_size - Max connections ≈ instances × workers × (pool_size + max_overflow) - pool_pre_ping and pool_use_lifo are always enabled to keep connections healthy and reduce churn. Config you can set (env vars or prefect.toml) - These control the API server’s SQLAlchemy pool:
Copy code
# Environment variables
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_SIZE=5
PREFECT_SERVER_DATABASE_SQLALCHEMY_MAX_OVERFLOW=10
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_RECYCLE=3600
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_TIMEOUT=30.0
Or in prefect.toml:
Copy code
[server.database.sqlalchemy]
pool_size = 5
max_overflow = 10
pool_recycle = 3600
pool_timeout = 30.0
Hardcoded (not configurable) - pool_pre_ping = True - pool_use_lifo = True Source links (Prefect 3.6.5) - Settings model (env var names and defaults): https://github.com/PrefectHQ/prefect/blob/3.6.5/src/prefect/settings/models/server/database.py - Engine/pool configuration (pre_ping, LIFO, etc.): https://github.com/PrefectHQ/prefect/blob/3.6.5/src/prefect/server/database/configurations.py Practical tips - Start with small pools (e.g., pool_size=5, max_overflow=10) and observe. Increase if you see pool timeouts; decrease if Postgres is connection-starved. - Remember to multiply by any process-level concurrency you use (e.g., multiple uvicorn workers). - If you front Postgres with PgBouncer, ensure settings are compatible with asyncpg and prepared statement caching. If you share how many API instances/workers you plan to run (and your Postgres max_connections), I can help you pick pool settings and a safe headroom.
j
I'm running up to 25 API servers using this command
Copy code
prefect server start --host 0.0.0.0 --port 4200 --no-services
And Postgres has 400 max connections @Marvin
m
thought for 96.9 seconds
Yes—connection pooling works fine with multiple API instances. In Prefect 3.6.5 the API uses a per-process SQLAlchemy async pool, so each API server maintains its own pool. Given your setup: - 25 API servers - Postgres max_connections = 400 - Defaults: pool_size=5, max_overflow=10 Your theoretical maximum DB connections from the API tier is: - 25 × (5 + 10) = 375 That fits under 400, but leaves very little headroom for Postgres background connections, migrations, admin tools, and any other clients. It will generally work, but it’s tight and you may see occasional pool timeouts or connection failures during spikes. Recommendations - Keep some buffer (20–30%) under Postgres max_connections. For 400, target ≤ 280–320 for Prefect. - Easiest lever is to reduce max_overflow: - Example: pool_size=5, max_overflow=5 → 25 × 10 = 250 max (good headroom) - Even more conservative: pool_size=4, max_overflow=6 → 25 × 10 = 250 - Only increase again if you observe pool timeouts under load. How to set (env vars)
Copy code
# Cap total connections while keeping solid baseline concurrency
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_SIZE=5
PREFECT_SERVER_DATABASE_SQLALCHEMY_MAX_OVERFLOW=5
# Optional tuning
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_RECYCLE=3600
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_TIMEOUT=30.0
Notes - Pools are per-process. If you ever run multiple workers per API process, multiply accordingly. - pool_pre_ping and pool_use_lifo are always enabled (good for health and reducing churn). - SQLAlchemy opens connections on demand; the “max” math above is for capacity planning. Helpful links (exact settings in source) - Settings (env var names and defaults): https://github.com/PrefectHQ/prefect/blob/3.6.5/src/prefect/settings/models/server/database.py - Engine/pool configuration (pre_ping, LIFO, etc.): https://github.com/PrefectHQ/prefect/blob/3.6.5/src/prefect/server/database/configurations.py If you can share whether each API server runs a single process (no extra workers) and your typical peak request rate, I can help dial in the exact pool_size/overflow combo and a safe headroom target.
j
@Marvin I'm running the server with the previous command, not sure how many workers are running by default
m
thought for 131.4 seconds
Great question. With that command, each “prefect server start” runs a single uvicorn worker in a single process. There’s no “workers” flag in Prefect 3.6.5’s server CLI, so your 25 processes = 25 API workers total. What this means for pooling - Pools are per process. With defaults (pool_size=5, max_overflow=10), your theoretical max is: - 25 × (5 + 10) = 375 connections - That fits under 400, but it leaves little headroom for Postgres itself and any other clients. Recommended tuning for 400 max_connections - Give yourself 20–30% headroom. An easy, safe cap is to reduce max_overflow: - Example: keep pool_size=5 and set max_overflow=5 → 25 × (5+5) = 250 max - Or pool_size=4 and max_overflow=6 → 25 × (4+6) = 250 max - Only increase if you observe pool timeouts at peak traffic. How to set it (env vars on each API container/VM)
Copy code
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_SIZE=5
PREFECT_SERVER_DATABASE_SQLALCHEMY_MAX_OVERFLOW=5
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_RECYCLE=3600
PREFECT_SERVER_DATABASE_SQLALCHEMY_POOL_TIMEOUT=30.0
Optional (prefect.toml)
Copy code
[server.database.sqlalchemy]
pool_size = 5
max_overflow = 5
pool_recycle = 3600
pool_timeout = 30.0
Quick Postgres check
Copy code
SELECT
  count(*) AS total,
  count(*) FILTER (WHERE state='active') AS active,
  count(*) FILTER (WHERE state='idle') AS idle
FROM pg_stat_activity;
For reference on the exact settings and defaults (verified in 3.6.5 source): - Settings model (env var names and defaults): https://github.com/PrefectHQ/prefect/blob/3.6.5/src/prefect/settings/models/server/database.py - Engine/pool behavior (pre_ping, LIFO): https://github.com/PrefectHQ/prefect/blob/3.6.5/src/prefect/server/database/configurations.py Follow-up question: Do you have a separate, single API instance running with services enabled (scheduler, late runs), or are all 25 started with --no-services? That affects how much DB headroom we should reserve.