TudCloud Inc.
All articles

Debug TCP Connection Failures: Conntrack, Listen Queues and Port Exhaustion

TudCloud Editorial5 min read
A server with glowing data lanes, a speed gauge and stacked data blocks representing bandwidth and traffic.

“The server cannot accept connections” describes several different failures. A full connection-tracking table, an application that is too slow to accept sockets, an exhausted file-descriptor limit and a client that cannot allocate a local port require different fixes. Increasing every network limit obscures the cause and can consume memory without restoring service.

Scope: a Linux VPS running a TCP service. Commands are read-only unless explicitly described otherwise. Use administrator privileges where required. On container hosts, inspect the relevant network namespace as well as the host; counters from the wrong namespace can look perfectly healthy.

Locate the failure before tuning the server

Test from an affected client and record whether DNS, TCP establishment, TLS or the application response fails. Replace the example URL with your actual endpoint.

curl --connect-timeout 5 --max-time 15 -o /dev/null -sS 
  -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} status=%{http_code}n' 
  https://example.com/

These are cumulative timestamps from the beginning of the transfer, not independent stage durations. Check curl’s exit status and error message too. A CDN-facing test measures the edge path; it may not establish a new connection to your origin. Pair it with origin logs and an authorized origin test.

On the VPS, confirm the expected listener exists. The example service port is HTTPS; use the actual application port.

sudo ss -lntp 'sport = :443'
ss -s
sudo journalctl -k --since '15 minutes ago' --no-pager

Distinguish the tables and queues

Resource What it represents Useful evidence
Conntrack table Tracked flows used by stateful filtering/NAT, including forwarded traffic Entry count, capacity, allocation/drop events
SYN/request queue Connections whose handshake is not yet complete SYN-RECV sockets and handshake observations
Accept queue Completed connections waiting for the application to accept them Listening socket queue depth and overflow counters
Process file descriptors Open sockets, files and other handles owned by a process Process limits and “too many open files” errors
Outbound local ports Source-port choices for locally initiated connections Client connection errors, destinations and socket churn

One request can depend on several of these resources: a reverse proxy accepts a browser connection and then creates a separate upstream connection. Healthy inbound queues do not establish that the proxy can connect to its backend.

Test the conntrack exhaustion hypothesis

sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
sudo journalctl -k --since '15 minutes ago' --no-pager | grep -i conntrack
sudo conntrack -S

The final command requires the distribution’s conntrack tools package. Missing sysctl paths can mean connection tracking is not loaded or not exposed in this environment; do not load modules just to make a diagnostic checklist pass.

nf_conntrack_count is the current number of allocated flow entries and nf_conntrack_max is the configured ceiling. Conntrack also has hash buckets and protocol-specific retention timeouts. A flow has original and reply-direction hash entries; do not double the flow count when calculating occupancy. See the kernel conntrack reference.

Take repeated samples during the incident. High occupancy together with new drop/allocation failures and matching kernel messages supports an exhaustion diagnosis. An isolated historical counter does not. conntrack -S output varies with kernel/tool version and can include per-CPU rows; compare like-for-like snapshots rather than reading one row as a global total.

Do not run conntrack -F as routine troubleshooting: flushing state can interrupt active NAT and firewall-dependent traffic. A higher ceiling also needs memory headroom. First investigate unexpected flow churn, short-lived upstream connections and whether connection pooling is working.

Check the accept queue independently

sudo ss -lntp 'sport = :443'
ss -nt state syn-recv 'sport = :443'
nstat -az TcpExtListenOverflows TcpExtListenDrops
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog

For a listening TCP socket, ss normally displays pending accepted-handshake connections in Recv-Q and its backlog limit in Send-Q. Repeated queue growth while overflow counters increase points toward an acceptance bottleneck. These TCP counters are namespace-wide, so another listener can contribute; correlate them with the target socket.

The application’s listen backlog and the kernel’s somaxconn interact. The SYN backlog is a different limit, and SYN cookies are not a substitute for sizing a legitimate service. Refer to the kernel TCP/IP settings documentation before altering these values.

A larger waiting room does not make workers faster. Check blocked event loops, backend timeouts, worker saturation and file-descriptor errors. If the application accepts connections quickly but responds slowly, the accept queue can remain small while users still experience severe latency.

Inspect the process and the outbound side

Find the responsible worker PID using ss or the service manager. Replace the example PID below; a master process may have different usage from its workers.

pid=12345
sudo cat "/proc/$pid/limits"
sudo ls -1 "/proc/$pid/fd" | wc -l
sysctl net.ipv4.ip_local_port_range
ss -tan state time-wait | head -n 30

The descriptor count is a moving snapshot. Look for application errors at the same time. A large TIME-WAIT population alone is not proof of port exhaustion. Port reuse depends on connection tuples and allocation behavior; it is not simply “one local port per connection across the whole server.” NAT gateways can also have their own allocation bottleneck outside the VPS.

For outbound failures, identify the destination concentration and exact error, then assess connection reuse, pool bounds and retry behavior. An aggressive retry loop can amplify a partial outage. Do not copy old advice to enable removed TCP settings or shorten timeouts globally.

A useful incident record

Keep a UTC time range, affected endpoint, client error, request rate, listener samples, counter deltas and relevant logs. After one targeted change, repeat the same observations and check both successful connections and response latency. Preserve previous values and a rollback method before any persistent tuning.

If packets never arrive at the guest, investigate the provider firewall and upstream path. If they arrive but application queues grow, focus on the service. For background reading, see our PSI and cgroup performance guide. Infrastructure upgrades should follow the measured bottleneck, not replace diagnosis.