netstat vs ss: Analyzing Network Connections and Listening Ports on Linux

Network tutorial - IT technology blog
Network tutorial - IT technology blog

When I first started managing servers, I’d see others using netstat and just follow along. It was a habit picked up from old tutorials, and I never questioned it. Until I needed to debug slow connections on a production server with a few thousand open sockets — netstat took over 30 seconds just to return results. That day, I finally took a serious look at ss.

I manage the network for a 50-person office and a small datacenter — this article is drawn from about 6 months of gradually transitioning from netstat to ss, and learning how to combine both tools for different scenarios.

netstat vs ss: Two Tools, One Goal

Both show you network connection states, listening ports, and active sockets. But how they work under the hood is completely different.

netstat is part of the net-tools package and reads information from /proc/net/ the traditional way. On Ubuntu 20.04+ and Debian 11+, it’s no longer pre-installed — you have to manually install it if you need it.

ss (socket statistics) is part of the iproute2 package and communicates directly with the kernel via netlink sockets. This is why it’s noticeably faster when there are many open connections — instead of parsing text files in /proc, it pulls data straight from the kernel.

Pros and Cons of Each Tool

netstat: Familiar but Outdated

Pros:

  • Syntax I remember without Googling; 2012 StackOverflow tutorials still work fine
  • Output is easy to read for beginners — clear columns, little explanation needed
  • View routing tables and interface statistics in one tool (though ip route does it better)

Cons:

  • Slow with many connections — parses /proc/net/tcp as plain text, doesn’t scale
  • The net-tools package is barely maintained; many distros have dropped it from default installs
  • Far fewer and less flexible filter options compared to ss

ss: Fast and Modern, but Takes Getting Used To

Pros:

  • On a server with 5,000+ sockets, ss returns results in under a second; netstat can take a full minute
  • Very powerful filter expression language — filter by IP, subnet, port, state, and process in a single command
  • Shows additional TCP timer and congestion window info — things netstat simply doesn’t have
  • Actively developed and is the standard tool on every modern Linux distro

Cons:

  • Filter syntax is completely different from netstat — takes a few sessions to get comfortable with
  • Output looks a bit unfamiliar at first, especially the Process column with its users:(("nginx",pid=5678,fd=7)) format

Which Tool for Which Situation?

After 6 months using both, I’ve settled on a fairly simple rule:

  • Production server with many connections: Use ss — no debate. On a server with thousands of open connections, ss returns results in seconds while netstat can take a full minute.
  • Quick debugging on a dev machine or workstation: Either works, but I’ve been gradually moving to ss to keep habits consistent.
  • Need routing tables or interface statistics: Use ip route and ip -s link from iproute2 instead of netstat -r — same package as ss, more consistent.

Practical Usage Guide

Check Listening Ports

The task I do most often — see which service is holding which port:

# Using ss (recommended)
ss -tlnp

# Flag explanation:
# -t : TCP only
# -l : listening sockets
# -n : show port numbers instead of service names (faster)
# -p : show process name and PID

# Equivalent netstat command
netstat -tlnp

Output from ss -tlnp looks like this:

State   Recv-Q  Send-Q  Local Address:Port  Peer Address:Port  Process
LISTEN  0       128     0.0.0.0:22          0.0.0.0:*          users:(("sshd",pid=1234,fd=3))
LISTEN  0       511     0.0.0.0:80          0.0.0.0:*          users:(("nginx",pid=5678,fd=6))
LISTEN  0       511     0.0.0.0:443         0.0.0.0:*          users:(("nginx",pid=5678,fd=7))

Find Which Process Is Holding a Specific Port

The most common scenario: a service fails to start because a port is already in use:

# Check which process is using port 8080
ss -tlnp | grep :8080

# Or use ss filter expression
ss -tlnp 'sport = :8080'

# Output will look like: users:(("node",pid=12345,fd=10))
# Kill that process with:
kill -9 12345

View All Active Connections

# All ESTABLISHED TCP connections
ss -tn state established

# Count connections by state
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn

# Quick overview with ss -s
ss -s

The ss -s output is very useful for a quick server health check:

Total: 234 (kernel 312)
TCP:   45 (estab 23, closed 5, orphaned 0, timewait 17)
UDP:   8

Filter Connections by IP or Subnet

I use this a lot when managing the office network — checking which machines are connecting to internal servers, or seeing what traffic from a specific IP is hitting which ports:

# Show all connections from subnet 192.168.1.0/24
ss -tn 'dst 192.168.1.0/24'

# Connections to a specific IP
ss -tn 'dst 192.168.1.100'

# Connections from a specific IP
ss -tn 'src 10.0.0.5'

# Combined: connections from subnet to port 443
ss -tn 'src 192.168.1.0/24 and dport = :443'

View TCP Timers — A Feature Exclusive to ss

# View TCP timers (keepalive, retransmit timeout...)
ss -tn -o

# Output will include an extra timer column:
# timer:(keepalive,1min3sec,0)
# Meaning: keepalive timer has 1 min 3 sec remaining, 0 retransmits

Real-time Monitoring

# Update every 2 seconds
watch -n 2 'ss -s'

# Or monitor connection count by state
watch -n 2 'ss -tan | awk "NR>1 {print \$1}" | sort | uniq -c | sort -rn'

Debugging Workflow for Abnormal Connections

When I discovered a server in the datacenter being port-scanned from an unknown IP, here’s the debug process I followed:

# Step 1: Check for SYN flood
ss -tn state syn-recv

# Step 2: Count connections by source IP — detect flooding IPs
ss -tn | awk 'NR>1 {print $5}' | grep -oP '^[\d.]+' | sort | uniq -c | sort -rn | head -20

# Step 3: View all connections from suspicious IP
ss -tn 'src 203.0.113.50'

# Step 4: Check for abnormal TIME_WAIT count
ss -tan state time-wait | wc -l

With netstat, step 2 would take a full minute when the server had many open connections. This is the real reason I switched to ss — not because some blog said it was great, but because waiting for output while debugging a live incident is genuinely frustrating.

Handy Aliases for Daily Use

These three aliases I add to ~/.bashrc on every server — saves quite a bit of typing:

# Add to ~/.bashrc
alias ports='ss -tlnp'                        # View listening ports
alias conns='ss -tn state established'         # Active connections
alias socksum='ss -s'                          # Socket statistics overview

# Reload
source ~/.bashrc

Installation If Not Already Present

# Debian/Ubuntu
sudo apt install iproute2     # ss (usually pre-installed)
sudo apt install net-tools    # netstat (if needed)

# CentOS/RHEL/Rocky Linux
sudo dnf install iproute      # ss
sudo dnf install net-tools    # netstat

# Check version
ss -V
netstat --version

On Ubuntu 22.04 and Debian 12, ss comes pre-installed. netstat does not — you’ll need to install it separately if required.

Conclusion

Starting from scratch? Learn ss from the start and skip netstat. The syntax is a bit different but worth the time investment. Already comfortable with netstat and want to switch? Memorizing a handful of core ss commands covers 90% of everyday tasks.

For me, after half a year running ss on production, I haven’t gone back to netstat. Not because anyone told me to — but because it’s faster and the filtering is more powerful exactly when I actually need it.

Share: