When a Linux server feels “slow under load,” the problem is often not CPU or memory—it’s what’s happening at the TCP connection layer. Every second, the kernel counts how many connections it initiates, how many it accepts, and how many fail. Those three numbers—active opens, passive opens, and failed connection attempts—are among the fastest ways to tell whether a box is a healthy client, a healthy server, or quietly dropping connections.
This article explains what each counter means, where to read it, how to turn the raw totals into a per-second rate, and how to interpret the patterns you’ll see in real production systems.
Table of Contents
The three counters at a glance
The Linux kernel exposes TCP statistics through SNMP-style counters. The three that describe connection establishment are:
| Counter | SNMP name | Meaning |
|---|---|---|
| Active opens | Tcp: ActiveOpens | Connections this host initiated (it sent the first SYN) |
| Passive opens | Tcp: PassiveOpens | Connections this host accepted (it received a SYN and completed the handshake) |
| Failed opens | Tcp: AttemptFails | Connection attempts that failed to reach the ESTABLISHED state |
A useful mental model:
- Active open = “I am a client.” Your app called
connect(). - Passive open = “I am a server.” Your app called
listen()/accept()and a client reached it. - Failed open = “something went wrong” before the connection was usable.
Most machines are predominantly one or the other. A web server shows mostly passive opens. A service that calls databases, caches, or upstream APIs shows mostly active opens. A machine doing both (like an API gateway) shows both.
Where to read the counters
Raw source: /proc/net/snmp
Everything ultimately comes from here:
cat /proc/net/snmp | grep ^Tcp
You’ll see two lines—a header and the values:
Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs ...
Tcp: 1 200 120000 -1 5829301 9481120 20733 15522 412 ...
These are cumulative counters since boot. A single reading tells you almost nothing—you need the rate of change.
The friendly tool: nstat
nstat is the right tool for humans. It reads the same counters and can show deltas over an interval:
# Snapshot of nonzero TCP counters
nstat -a | grep -i tcp
# Reset the baseline, wait, then show what changed
nstat -n; sleep 10; nstat
Key fields map directly:
See also: Mastering the Linux Command Line — Your Complete Free Training Guide
TcpActiveOpensTcpPassiveOpensTcpAttemptFailsTcpEstabResetsTcpCurrEstab
The classic: netstat -s / ss -s
netstat -s | grep -A3 -i 'connection'
Produces human-readable lines like “5829301 active connection openings” and “20733 failed connection attempts.” Same numbers, nicer labels.
Turning totals into a rate (the part that matters)
Because the counters only ever increase, the meaningful signal is opens per second. Compute it by sampling twice:
# Simple per-second rate over a 10s window
A1=$(awk '/^Tcp:/{print $6}' /proc/net/snmp | tail -1) # ActiveOpens
sleep 10
A2=$(awk '/^Tcp:/{print $6}' /proc/net/snmp | tail -1)
echo "Active opens/sec: $(( (A2 - A1) / 10 ))"
Or just let nstat do the differencing:
nstat -n; sleep 10; nstat | awk '/TcpActiveOpens|TcpPassiveOpens|TcpAttemptFails/{print $1, $2/10 "/s"}'
In real monitoring (Prometheus node_exporter, Telegraf, etc.), these are already exposed as counters—graph the rate() and you get the same thing continuously.
Reading the patterns
Here’s how to interpret what you see.
Healthy server
TcpPassiveOpens high and steady
TcpActiveOpens low
TcpAttemptFails near zero
The box mostly accepts connections. A low, flat AttemptFails is normal—some clients always disconnect early.
Healthy client / worker
TcpActiveOpens high
TcpPassiveOpens low
TcpAttemptFails near zero
Typical of a service that fans out to backends. If ActiveOpens/sec is very high relative to your throughput, suspect missing connection pooling—you may be opening a fresh TCP connection per request instead of reusing them.
Rising failed opens — the red flag
TcpAttemptFails climbing
AttemptFails increments when a connection attempt never reaches ESTABLISHED. Common causes:
- Connection refused — nothing listening on the target port (client-side active-open failures).
- SYN timeouts — the peer or network dropped the SYN; firewall blackholing.
- Backlog overflow on the server — the listen queue is full, so incoming handshakes are dropped (often paired with
ListenOverflows/ListenDrops).
Correlate with these companion counters:
nstat -az | grep -Ei 'ListenOverflows|ListenDrops|TCPSynRetrans|TcpAttemptFails'
If TcpExtListenOverflows is rising alongside AttemptFails, your server is accepting connections faster than the application can accept() them—tune the listen backlog and net.core.somaxconn, and check whether the app’s accept loop is blocked.
Connection churn
High TcpActiveOpens and high TcpEstabResets together means connections are being set up and torn down rapidly. That’s expensive—each open costs a handshake and each reset is an abrupt teardown. Look for:
- Missing or too-small connection pools.
- Aggressive client timeouts causing resets.
- Keep-alive disabled where it should be on.
Common causes of failed connection openings
- Listen backlog too small. Bursty traffic overflows the accept queue. Raise
net.core.somaxconnand pass a larger backlog tolisten(). - Ephemeral port exhaustion. A client opening huge numbers of active connections can run out of source ports. Check
net.ipv4.ip_local_port_rangeand reduce churn with pooling. - Firewall / security group dropping SYNs. Manifests as SYN retransmits and eventual
AttemptFailswith noRST. - Upstream service down or overloaded.
connect()gets refused or times out. SYNflood or scanning. A spike in half-open attempts; SYN cookies (net.ipv4.tcp_syncookies) help absorb it.
A quick diagnostic recipe
When connection performance is suspect, run this and read the deltas:
# 1. Baseline, wait through a representative window, then diff
nstat -n; sleep 30; nstat | grep -Ei 'ActiveOpens|PassiveOpens|AttemptFails|EstabResets|CurrEstab'
# 2. Look for queue drops that explain failures
nstat -az | grep -Ei 'ListenOverflows|ListenDrops|SynRetrans'
# 3. Confirm listening sockets and their backlog usage
ss -ltn # Recv-Q vs Send-Q on listeners shows backlog pressure
# 4. Check the relevant tunables
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog \
net.ipv4.ip_local_port_range net.ipv4.tcp_syncookies
Interpretation:
- Failures with
ListenOverflows→ backlog/accept problem on this server. - Failures with
SynRetrans, no overflow → network/firewall or dead upstream. - High active opens, modest work done → add connection pooling.
Key tunables worth knowing
| Sysctl | Why it matters |
|---|---|
net.core.somaxconn | Caps the effective listen backlog |
net.ipv4.tcp_max_syn_backlog | Size of the SYN (half-open) queue |
net.ipv4.ip_local_port_range | How many ephemeral ports clients can use |
net.ipv4.tcp_syncookies | Survives SYN floods without dropping legit clients |
net.ipv4.tcp_tw_reuse | Reuses TIME_WAIT sockets for new active opens |
Change these only with a measured reason—defaults are fine for many workloads, and blindly raising backlogs can mask an application that simply isn’t accepting fast enough.
Bottom line
The active/passive/failed open counters are a compact health check for TCP connection performance:
- Active opens tell you the host is acting as a client.
- Passive opens tell you it’s acting as a server.
- Failed opens (
AttemptFails) are the alarm—track their rate, not the raw total, and correlate withListenOverflowsand SYN retransmits to find the cause.
Read them with nstat over a fixed interval, graph the per-second rate in your monitoring stack, and you’ll spot backlog overflows, missing connection pools, and network black holes long before they turn into a user-visible outage.



