Shared clients and database pools should be initialised and closed through the application lifecycle. Creating an uncontrolled pool for every request can exhaust connections and leave resources behind.
IN THIS ARTICLE
What you need before you begin
Identify the resources required by each worker and the connection limits of their services. Check how the installed application uses the supported FastAPI lifespan mechanism.
01 Manage FastAPI lifespan resources and database pools
Create the required clients or pools in a lifespan context and make them available through the application’s dependency pattern.
Close the resources during lifespan shutdown. Avoid assuming that a module import provides a complete startup and cleanup lifecycle.
Set finite pool sizes and connection timeouts. Multiply those limits by the number of worker processes when checking the total database or API demand.
Keep startup checks limited and observable. A failed essential dependency can prevent readiness, while optional services may need a deliberate degraded mode.
Test startup, normal requests and graceful shutdown, including a deployment that replaces the running processes.
02 Example: open and close one bounded client per worker
This FastAPI lifespan pattern creates a reusable HTTPX client for each application process and closes it at shutdown. Merge it into the project's existing lifecycle rather than creating another app object alongside it.
from contextlib import asynccontextmanager
import httpx
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
limits = httpx.Limits(
max_connections=10, max_keepalive_connections=5
)
async with httpx.AsyncClient(timeout=10.0, limits=limits) as client:
app.state.http_client = client
yield
app = FastAPI(lifespan=lifespan)
Application dependencies can retrieve this client from the request's app state for approved outgoing calls. The example does not issue any external request. Its limits apply per worker, so four workers could create four separate pools.
Test startup, a controlled request and graceful shutdown. Verify that resources close after shutdown and a new worker creates a fresh usable client. Apply the same deliberate lifecycle principle using your database library's own supported pool and disposal API.
Confirm the expected result
Observe the expected number of database connections while idle and under load. Confirm they close after the service stops and that a restarted worker creates fresh usable resources.
Troubleshoot unexpected results
A connection count that grows per request can indicate clients are repeatedly created without cleanup. Inspect the dependency lifecycle before merely increasing the database limit.
A common question
Will one lifespan-created object be shared by every worker?
Each process normally has its own memory and resources. Budget pools and large loaded objects per worker unless the architecture explicitly uses an external shared service.