idle_session_timeout
idle_session_timeout sets the maximum time a session may sit idle — waiting for the next client query — WHEN NOT inside a transaction.
Tier 1 — Lab-Proven — Measured behavior and safety boundaries are available below.
Measured Effect
summary
MEASURED the idle cap terminating a session and a shorter idle surviving. A session connected, set idle_session_timeout, ran one query, then sat idle (state idle, NOT in a transaction); a monitor read pg_stat_activity to check whether the backend survived the idle period.
observations
| idle session timeout | idle for | outcome | note |
|---|---|---|---|
| 3s | ~6s (not in txn) | TERMINATED (FATAL idle-session timeout) | smoking gun: backend gone after idling past the cap |
| 5s | ~2s (not in txn) | SURVIVED | idle under the cap -> connection kept |
session survives
It does NOT survive once the cap is exceeded — like idle_in_transaction_session_timeout (#25) it is FATAL: the backend is terminated and the connection closed. The server log shows FATAL: terminating connection due to idle-session timeout. This is the opposite of statement_timeout (#26)/lock_timeout (#27), which cancel a statement non-fatally and keep the session.
reading
It is a hard idle reaper, not a performance dial. A session idle (between transactions) past the limit is killed; the client only learns of it on its next query (server closed the connection unexpectedly). It is purely about reclaiming connection slots/resources held by abandoned idle connections — it has no effect on running statements, on locks, or on the xmin horizon.
honesty note
idle_session_timeout is a server-side timer; the chosen seconds are lab values, the mechanism (terminate-on-idle-exceeded, FATAL, connection closed, ONLY when not in a transaction) is deterministic and box-independent. Survival is observed via pg_stat_activity rather than wall-clock so there is no exec-overhead caveat here.
Reproduced Danger
Sizing / Safe Range
Workload Guidance
Unlock Incident Pro
The free sections tell you what to do. Pro is the measured proof — the exact failure signature, safe-ceiling formula, and per-workload defaults, each captured on real PG 14–18 runs.
- Reproduced Danger
- Sizing / Safe Range
- Workload Guidance
idle_session_timeout sets the maximum time a session may sit idle — waiting for the next client query — WHEN NOT inside a transaction. If a connection stays idle (state idle) longer than the limit, the backend is terminated with a FATAL error — FATAL: terminating connection due to idle-session timeout — closing the connection; the client only discovers it on its next query. The default is 0, meaning sessions may stay idle forever. Two scope facts are essential: (1) it counts ONLY idle time between transactions, not idle time INSIDE an open transaction — that is idle_in_transaction_session_timeout's job; and (2) it is FATAL (session terminated), like idle_in_transaction_session_timeout and unlike the non-fatal statement_timeout/lock_timeout. Its purpose is operational hygiene: reclaim connection slots and server resources held by abandoned or forgotten idle connections, rather than data-correctness or bloat control. Because context=user it can be set per session, role, database, or cluster-wide and takes effect immediately. It exists on PG 14-18 (no version cliff).
- name
idle_session_timeout
- setting default
0 (disabled — idle forever)
- unit
ms
- vartype
integer
- category
Client Connection Defaults / Statement Behavior
- context
user
- min val
0
- max val
2147483647
- enumvals
(n/a — integer ms)
- boot val
0
- reset val
0
- pending restart
false
- short desc
Sets the maximum allowed idle time between queries, when not in a transaction.
- ground truth note
Captured literally from pg_settings on PG 14-18 (-18) on 2026-06-20. setting=0, boot_val=0, reset_val=0 on EVERY version (default_changed=false) — the guard ships DISABLED. context=user (session-changeable, no reload/restart). vartype=integer, unit=ms, min_val=0 (0 disables), max_val=2147483647. category='Client Connection Defaults / Statement Behavior', short_desc='Sets the maximum allowed idle time between queries, when not in a transaction.' Identical across 14, 15, 16, 17, 18.
Rock-stable across versions: default=0 (disabled), min=0, max=2147483647, context=user, vartype=integer, unit=ms on PG 14, 15, 16, 17 and 18 — no version cliff (default_changed=false). Note the family asymmetry: idle_session_timeout and idle_in_transaction_session_timeout exist on all of 14-18, statement_timeout/lock_timeout likewise, but transaction_timeout is NEW in PG 17. Per-version detail in _guc_matrix_machine.
context=user: set it per session with SET idle_session_timeout = '10min'; (effective immediately), or per role/database with ALTER ROLE/DATABASE . SET (the usual place — e.g. reap idle interactive sessions for a reporting role), or cluster-wide via postgresql.conf / ALTER SYSTEM + reload. CONTEXT PROOF (captured literal, identical on and): context=user, so a session SET is accepted with no wrong-context error. BOUNDARY PROOF (captured literal, identical on and): a negative value is rejected at set time -- ALTER SYSTEM SET idle_session_timeout = -1; -> ERROR: -1 ms is outside the valid range for parameter "idle_session_timeout" (0 ms .. 2147483647 ms). The valid range is 0 . 2147483647 ms; 0 DISABLES the cap (idle forever, the default). The value is in milliseconds but accepts unit suffixes like '10min', '30s', '500ms'.
Managed providers (Azure Database for PostgreSQL Flexible Server, Amazon RDS / Aurora, Cloud SQL, Crunchy, Supabase, Neon, Heroku) expose idle_session_timeout as a server parameter, default 0, and because context=user you can set it per-session/role/database without provider involvement. Many of these run a connection pooler (or you do), so the main caution applies: set idle_session_timeout LONGER than the pool's idle-recycle interval, or scope it to interactive/admin roles, so it does not reap healthy pooled connections. Recommended posture: a generous value (minutes) for human/interactive roles to reclaim abandoned sessions, left unset (or generous) for pooled application roles, and ALWAYS paired with idle_in_transaction_session_timeout for the actual bloat/snapshot guard. Do not treat idle_session_timeout as a substitute for idle_in_transaction_session_timeout.
Common Pitfalls
- Assuming idle_session_timeout guards against idle-in-transaction bloat — it does NOT; it ignores in-transaction idle. Use idle_in_transaction_session_timeout for that.
- Setting it below a connection pool's idle interval — the server reaps pooled idle connections, and the app then borrows dead connections (intermittent 'server closed the connection').
- Forgetting it is FATAL (session terminated), not a per-statement cancel — clients are disconnected and must reconnect.
- Using it on heavily-pooled application roles where the pool already recycles idle connections — usually redundant and risky there.
- Setting one aggressive global value instead of per-role — interactive roles want reaping, pooled app roles do not.
- Expecting it to bound running statements or whole transactions — it only counts BETWEEN-transaction idle time.
Evidence and References
- PostgreSQL docs: Runtime Config — Client Connection Defaults / Statement Behavior (idle_session_timeout — terminate any session idle longer than the limit; note it does NOT apply when within an open transaction; 0 disables).
- PostgreSQL docs: idle_in_transaction_session_timeout (the sibling that DOES bound idle time inside a transaction — the snapshot-pinning/bloat case idle_session_timeout ignores).
- PostgreSQL docs: pg_stat_activity — the
statecolumn (idlevsidle in transaction) that distinguishes which of the two idle timeouts applies. - PostgreSQL docs: statement_timeout, lock_timeout, transaction_timeout (the rest of the timeout family — run time, lock wait, and total transaction respectively; transaction_timeout new in PG 17).
- Lab capture 2026-06-20: — a session idle ~6s (not in a txn) under idle_session_timeout=3s was terminated (FATAL idle-session timeout) while idle ~2s under 5s survived ; a tight cap reaped an idle pooled connection, and an idle-in-transaction session survived idle_session_timeout=2s but was terminated by idle_in_transaction_session_timeout=2s ; boundary min=0 disables, range error on -1, context=user, identical /; pg_settings ground truth PG 14-18 (-18).
Technical Reference
- Category
- Client Connection Defaults / Statement Behavior
- Type
- integer (ms (milliseconds; 0 disables))
- Blast radius
- session
- Severity
- Moderate
- Context
- user
- Versions
- 14, 15, 16, 17, 18
- Reviewed
- 2026-06-20
Severity scale: 1 Minimal · 2 Low · 3 Moderate · 4 High · 5 Critical.