July 23, 2026
The Linux Command That Quietly Became My Favorite Debugging Tool
Reliable, fast, and always available

By Fateyaly
5 min read
The first time I encountered a production issue, I reached for every tool except the one that could have solved it in under a minute.
I opened multiple terminal tabs, checked application logs, restarted services, searched through configuration files, and even considered rebooting the server. The problem seemed complicated.
It wasn't.
One command exposed the root cause almost immediately.
That command was ss.
Unlike many Linux utilities that receive endless attention, ss rarely appears in tutorials or "top Linux commands" lists. Yet it has quietly become one of the most valuable debugging tools in my workflow. Whether I'm troubleshooting a web server, debugging an API, investigating database connectivity, or analyzing suspicious network activity, ss is almost always the first command I run.
It answers one deceptively simple question:
What is happening with the system's network sockets right now?
In many cases, that single answer is enough to explain an entire incident.
Why Network Sockets Matter More Than You Think
Every networked application communicates through sockets.
When you SSH into a server…
When Nginx accepts an HTTP request…
When PostgreSQL receives a database connection…
When Docker exposes a container…
When a reverse shell connects back to an attacker…
Sockets make it possible.
If those sockets aren't behaving as expected, your application probably isn't either.
The beauty of ss is that it lets you inspect these connections directly from the kernel's networking stack without relying on assumptions.
Why ss Replaced netstat
For years, Linux administrators relied on netstat.
It still works on many systems, but modern Linux distributions recommend ss, which is part of the iproute2 suite.
Why?
Because ss is significantly faster.
Instead of parsing information from /proc like older networking tools, ss communicates efficiently with the kernel using Netlink sockets. On systems handling tens of thousands of connections, the difference is immediately noticeable.
Large servers that make netstat struggle often return results from ss almost instantly.
When you're debugging a production incident, every second matters.
The First Command I Run
When someone says:
"The server isn't responding."
I don't immediately check the application.
I check the sockets.
ss -tulnss -tulnLet's break it down.
- -t → TCP sockets
- -u → UDP sockets
- -l → Listening services
- -n → Show numeric IP addresses and ports
Example output:
Netid State Local Address:Port
tcp LISTEN 0.0.0.0:22
tcp LISTEN 0.0.0.0:80
tcp LISTEN 127.0.0.1:5432Netid State Local Address:Port
tcp LISTEN 0.0.0.0:22
tcp LISTEN 0.0.0.0:80
tcp LISTEN 127.0.0.1:5432Within seconds I already know:
- SSH is listening.
- The web server is accepting connections.
- PostgreSQL only accepts local connections.
That's valuable information before I even inspect a single log file.
Is My Application Actually Listening?
One of the most common deployment mistakes is assuming an application started successfully.
Systemd says the service is active.
The process exists.
But users still can't connect.
Why?
Because the application never opened its listening socket.
Instead of guessing:
ss -ltnss -ltnSuppose you expect your API to listen on port 8080.
Expected:
LISTEN 0 4096 *:8080LISTEN 0 4096 *:8080Actual:
Nothing.
Now you know the application isn't listening at all.
No firewall investigation.
No DNS debugging.
No packet captures.
The service simply failed to bind to the port.
Finding the Process Behind a Port
Sometimes you know the port.
You don't know the process.
ss -tulpnss -tulpnExample:
tcp LISTEN 0 128 *:443 users:(("nginx",pid=1214,fd=6))tcp LISTEN 0 128 *:443 users:(("nginx",pid=1214,fd=6))Immediately you know:
- Process name
- Process ID
- File descriptor
- Listening port
If something unexpected is listening on a production server, this command usually reveals it within seconds.
Debugging "Address Already in Use"
Almost every Linux developer has seen this error.
bind(): Address already in usebind(): Address already in useThe application refuses to start because another process already owns the port.
Instead of searching manually:
ss -ltnp | grep :8080ss -ltnp | grep :8080Output:
LISTEN 0 128 *:8080 users:(("java",pid=8342))LISTEN 0 128 *:8080 users:(("java",pid=8342))Problem solved.
The existing Java process already occupies the port.
No guessing required.
Investigating Connection Floods
Suppose your API suddenly becomes slow.
CPU usage looks normal.
Memory usage looks fine.
Disk isn't busy.
What now?
Inspect established connections.
ss -tanss -tanExample:
ESTAB
ESTAB
ESTAB
ESTAB
TIME-WAIT
TIME-WAIT
SYN-RECVESTAB
ESTAB
ESTAB
ESTAB
TIME-WAIT
TIME-WAIT
SYN-RECVHundreds or thousands of sockets stuck in SYN-RECV can indicate:
- SYN flood attacks
- Reverse proxy issues
- Network congestion
- Firewall interference
A huge number of TIME-WAIT sockets may suggest:
- Extremely high request rates
- Inefficient connection reuse
- Missing HTTP keep-alive
- Poor load balancer configuration
Without opening a single application log, you've already narrowed the investigation considerably.
Detecting Database Bottlenecks
Imagine PostgreSQL starts rejecting new clients.
The database appears healthy.
CPU usage remains low.
Memory looks normal.
Run:
ss -tan | grep :5432ss -tan | grep :5432If hundreds of client connections remain established, the problem may not be PostgreSQL itself.
Instead, an application could be leaking database connections because it isn't returning them to the connection pool.
Application debugging suddenly becomes much easier because you've identified the likely failure domain.
Watching Connections in Real Time
Sometimes static output isn't enough.
Linux lets you monitor socket activity continuously.
watch -n 1 'ss -tan'watch -n 1 'ss -tan'Every second you'll see
- New client connections
- Closed sessions
- Increasing connection counts
- Changing socket states
This becomes incredibly useful during:
- Load testing
- Performance tuning
- Incident response
- DDoS investigations
Watching the network evolve live often reveals patterns that snapshots miss.
When Containers Refuse to Communicate
Containerized environments introduce another layer of networking complexity.
Suppose an Nginx container can't reach a backend API.
Instead of immediately blaming Docker networking, inspect the sockets.
ss -tulpnss -tulpnIf the backend service only listens on:
127.0.0.1:5000127.0.0.1:5000the container cannot access it from another network namespace.
Changing the application to bind to:
0.0.0.0:50000.0.0.0:5000may resolve the issue instantly.
The problem wasn't Docker.
It was the listening interface.
A Security Perspective
ss isn't just useful for system administrators.
Security professionals rely on it constantly.
Unexpected outbound connections often reveal:
- Malware
- Reverse shells
- Unauthorized remote access tools
- Suspicious persistence mechanisms
For example:
ss -tpss -tpmight show:
ESTAB 192.168.1.10:49782 198.51.100.24:4444
users:(("bash",pid=4123))ESTAB 192.168.1.10:49782 198.51.100.24:4444
users:(("bash",pid=4123))A Bash process maintaining an outbound TCP session deserves immediate investigation.
Pairing ss with tools like:
lsof
ps
journalctl
tcpdumplsof
ps
journalctl
tcpdumpcreates an effective incident-response workflow that requires no additional software.
The Command Is Only the Beginning
One mistake beginners make is treating ss as a magic command.
It isn't.
It's an observation tool.
The real skill lies in asking better questions.
Instead of:
"Why doesn't my website work?"
Ask:
- Is anything listening?
- Which process owns the port?
- Are clients connecting?
- Are connections completing?
- Are sockets stuck in unusual states?
- Are unexpected outbound sessions present?
Every answer reduces uncertainty.
That's exactly what debugging is supposed to do.
My Go-To ss Cheat Sheet
These are the commands I use most frequently.
List listening TCP and UDP services
ss -tulnss -tulnShow listening services with process names
ss -tulpnss -tulpnDisplay all established TCP connections
ss -tanss -tanFind which process owns a specific port
ss -ltnp | grep :443ss -ltnp | grep :443Inspect active SSH connections
ss -tn | grep :22ss -tn | grep :22Monitor connections live
watch -n 1 'ss -tan'watch -n 1 'ss -tan'These six commands solve a surprising number of real-world debugging problems.
Final Thoughts
Linux offers an incredible ecosystem of observability tools. Packet analyzers like tcpdump, performance profilers such as perf, tracing frameworks like eBPF, and logging systems powered by systemd all have their place.
Yet one of the tools I rely on most is far simpler.
ss doesn't generate dashboards.
It doesn't produce colorful graphs.
It doesn't require installation or configuration.
It simply asks the kernel one question:
"What is happening with the network right now?"
More often than not, the answer points directly toward the root cause.
The longer I've worked with Linux, the more I've realized that effective debugging isn't about collecting dozens of tools, it's about mastering the few that consistently reveal the truth.
For me, ss quietly became one of those tools.