PostgreSQL Error Guide: 'SSL connection has been closed unexpectedly' TLS Disconnects
Fix PostgreSQL 'SSL connection has been closed unexpectedly': diagnose idle timeouts, server crashes, OOM kills, and network resets behind TLS disconnects.
- #postgres
- #troubleshooting
- #errors
- #networking
Exact Error Message
psql: error: connection to server at "db.internal" (10.0.4.12), port 5432 failed:
SSL connection has been closed unexpectedly
From a driver mid-query, it often appears as:
ERROR: server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
SSL connection has been closed unexpectedly
What the Error Means
This error means the encrypted TLS connection between the client and PostgreSQL was torn down without a clean close handshake. The client expected more data (or to keep the session alive) but the underlying socket was dropped. The keyword is “unexpectedly” — a normal disconnect closes the TLS session gracefully, while this indicates the connection vanished mid-stream.
Crucially, the message is usually a symptom rather than a root cause. The TLS layer is simply reporting that the TCP connection died. The real reason is almost always one layer down: the server backend crashed, was killed, the connection idled out at a proxy or firewall, the network reset the socket, or the server hit a resource limit. To fix it you must look past the SSL wording and find what actually closed the socket — frequently in the PostgreSQL server log or the OS.
Common Causes
- A crashed or killed backend. If a backend process segfaults or the postmaster restarts after another backend crashes, all SSL connections drop abruptly.
- OOM killer terminating PostgreSQL. Under memory pressure, the Linux OOM killer may kill a backend or the postmaster, severing connections.
- Idle connection timeouts at a proxy/firewall. Load balancers (HAProxy, AWS NLB/RDS proxy), NAT gateways, or firewalls silently drop idle TCP connections, and the next use fails with this error.
- Network resets or transient failures. A flapping link, MTU mismatch, or stateful firewall reboot resets established connections.
- TLS renegotiation or version mismatch. Incompatible TLS versions/ciphers, or an expired/rotated server certificate, can break the session.
statement_timeoutplus a dropped result. A canceled long query combined with an aggressive proxy can surface as a closed SSL connection on the client side.
How to Reproduce the Error
Establish an SSL connection, then have the server side terminate the backend while the client is mid-session. In one session, find your PID:
SELECT pg_backend_pid(); -- e.g. 20417
From an admin session, terminate it (simulating a crash/kill):
SELECT pg_terminate_backend(20417);
Back in the first session, the next query fails:
SELECT 1;
-- SSL connection has been closed unexpectedly
A proxy idle timeout reproduces similarly: open a connection, leave it idle past the proxy’s timeout, then run any query.
Diagnostic Commands
Confirm the server is up and reachable at all:
pg_isready -h db.internal -p 5432
Check whether SSL is enabled and what TLS version is negotiated (read-only):
psql "host=db.internal sslmode=require" -c "SELECT ssl, version, cipher FROM pg_stat_ssl WHERE pid = pg_backend_pid();"
Look for backend crashes, restarts, or OOM kills in the server log:
sudo journalctl -u postgresql --no-pager | grep -iE "terminated|crash|restart|out of memory|killed" | tail -40
Check the OS for OOM-killer activity:
sudo dmesg | grep -iE "oom|killed process" | tail -20
Inspect established connections and look for unexpected drops:
ss -tnp state established '( dport = :5432 or sport = :5432 )' | head
Review certificate expiry on the server cert (read-only):
openssl x509 -enddate -noout -in /var/lib/postgresql/server.crt
Step-by-Step Resolution
-
Determine if it is one connection or all of them. If every connection drops at once, the server likely crashed or restarted — check the server log and
dmesg. If only idle connections drop, suspect a proxy/firewall timeout. -
Check for OOM kills. Run the
dmesggrep above. If the OOM killer is terminating PostgreSQL, reducework_mem/max_connectionsor add memory; consider setting the postmaster’s OOM score adjustment so backends are killed before the postmaster. -
Inspect proxy and firewall idle timeouts. Compare your load balancer/NAT idle timeout against how long connections sit idle. Enable TCP keepalives so the kernel keeps the connection warm: set
tcp_keepalives_idle,tcp_keepalives_interval, andtcp_keepalives_countin PostgreSQL, or equivalent client settings. -
Add connection validation in the pool. Configure your connection pool (PgBouncer, HikariCP, etc.) to test connections before handing them out (a lightweight
SELECT 1) so dead connections are discarded rather than handed to the application. -
Verify certificates and TLS settings. Check the server certificate expiry and that client/server agree on TLS version and ciphers. Rotate expired certs and align
ssl_min_protocol_version. -
Add retry logic for transient drops. Network blips happen; wrap connection acquisition and idempotent operations in retry-with-reconnect so a single dropped socket does not surface as a user error.
Prevention and Best Practices
- Enable TCP keepalives on both server and client so idle connections are kept alive or detected promptly, beating proxy idle timeouts.
- Use a connection pooler with test-on-borrow validation so stale connections never reach the application.
- Monitor memory and tune
work_mem/max_connectionsto avoid OOM kills; set the postmaster OOM adjustment to protect it. - Keep proxy idle timeouts longer than your pool’s idle recycle interval, or recycle pool connections more aggressively than the proxy drops them.
- Track certificate expiry and automate rotation so TLS never breaks on an expired cert.
- For triage, the free incident assistant can correlate an SSL-closed log block with a backend crash, OOM event, or proxy timeout.
Related Errors
server closed the connection unexpectedly— the non-SSL phrasing of the same underlying socket drop.could not connect to server: Connection refused— the server is down or not listening, a different failure mode.terminating connection due to administrator command— an explicitpg_terminate_backend.connection to server ... failed: timeout expired— the network path is up but unresponsive.
Frequently Asked Questions
Is this an SSL/TLS problem or a network problem? Usually a network or server problem reported by the TLS layer. The SSL stack notices the TCP connection died and reports it. Only when certificates, TLS versions, or ciphers are misconfigured is SSL itself the root cause — check those, but look at server crashes and idle timeouts first.
Why do my connections drop only after being idle for a few minutes? That signature points to a proxy, NAT gateway, or firewall with an idle timeout silently dropping the TCP connection. The next query on that connection fails. Enable TCP keepalives and use pool connection validation to avoid handing out dead connections.
Could the OOM killer be causing this?
Yes. If Linux kills a PostgreSQL backend or the postmaster under memory pressure, connections drop abruptly with this error. Check dmesg for OOM messages and reduce memory pressure or protect the postmaster’s OOM score.
Does setting sslmode=disable fix it? Disabling SSL only changes the error wording to the plain “server closed the connection unexpectedly” — it does not fix the underlying drop and removes encryption. Keep SSL and address the real cause (crash, OOM, or idle timeout).
How do TCP keepalives help?
Keepalives send periodic probes on idle connections, which both keeps stateful intermediaries from expiring the connection and lets the kernel detect a truly dead peer quickly. Tuning tcp_keepalives_idle below your proxy’s idle timeout prevents silent drops.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.