1.The connection lifecycle, and `CONN_MAX_AGE`
By default Django opens a database connection when a request first needs one and closes it when the response is finished. That is `CONN_MAX_AGE = 0`, and it means every request pays to establish a connection — a TCP handshake, authentication, and on PostgreSQL a new backend process. Setting `CONN_MAX_AGE` to a positive number of seconds keeps the connection open for reuse across requests; `None` keeps it forever. The catch is that a reused connection can have been closed at the other end while it sat idle, which is what `CONN_HEALTH_CHECKS = True` exists to handle.
Key features
- `CONN_MAX_AGE` default is `0` — "preserving the historical behavior of closing the database connection at the end of each request"
- A positive value keeps the connection for that many seconds; `None` means "unlimited persistent connections"
- The age is only evaluated between requests, so a long request never has its connection closed underneath it
- `CONN_HEALTH_CHECKS = True` prevents errors "when a connection has been closed by the database server", checking once per request and only if the database is accessed
- With persistence on, connection count tracks worker *processes*, not concurrent requests — a different and much larger number
- It is per process: 8 workers with `CONN_MAX_AGE=60` hold 8 connections even while idle
What each setting actually changes
- req 1 · 0 ms: CONN_MAX_AGE = 0: connect — TCP handshake, auth, a new PostgreSQL backend process
- req 1 · end: close — the connection is discarded — nothing is kept
- req 2: connect again, close again — the same setup cost, paid per request forever
- req 1 · 0 ms: CONN_MAX_AGE = 60: connect once — the same setup, but only this time
- req 2 · req 3: reuse — no setup at all — and the connection stays parked between requests, even while idle
- idle > 60 s: closed at the *start* of the next request — age is checked between requests; a long request is never cut off mid-flight
- after a DB restart: the parked connection is dead — CONN_HEALTH_CHECKS notices and reconnects; without it, the next query errors
Command
DATABASES["default"]["CONN_MAX_AGE"] = 60 # seconds; 0 = per request, None = forever
DATABASES["default"]["CONN_HEALTH_CHECKS"] = True`CONN_MAX_AGE = 0` is the default and means a connection per request — expensive but self-limiting, because the count tracks in-flight requests. A positive value changes the denominator to worker *processes*, which is a larger and constant number, so do the multiplication across web, Celery, beat and ad-hoc processes before enabling it. Always pair persistence with `CONN_HEALTH_CHECKS = True`, since a parked connection can be closed at the other end by an idle timeout or a failover, and prefer a bounded age over `None` so connections recycle on their own.