Inspect Ports with ss and netstat
Learn how to inspect open ports and listening sockets on Linux using ss and netstat. Practical commands, troubleshooting tips, and what to study next—all in 320 characters.
Focus: inspect ports with ss and netstat
You're staring at a connection refused error, a port already in use message, or a service that's silently not listening where it should be. Without the ability to quickly inspect which ports are open, which sockets are in a listening state, and which processes own them, debugging network issues becomes a guessing game. This lesson gives you the precise tools—ss and netstat—to inspect ports with confidence, turning network mysteries into clear answers.
The problem this lesson solves
Every network service on Linux, from a web server on port 80 to a database on 5432, announces its availability by opening a socket and entering a listening state. When things go wrong—a service isn't reachable, a port is already taken, or a firewall is blocking traffic—the first step is usually to inspect what's happening at the port level.
Consider these all-too-common scenarios:
- Port already in use: You start a new service and get
Address already in use. Which process is hogging the port? - Service not responding: Your API is running, but clients get connection refused. Is it actually listening?
- Security audit: You need to inventory all open ports to spot unexpected listeners.
Guessing won't cut it. You need a systematic way to inspect ports — to see exactly what's listening, on which interface, and which process owns it. That's where ss and netstat come in.
These tools read the kernel's socket table and present it in a human-friendly format. They're the network equivalent of ps for processes, giving you a live snapshot of socket activity.
Core concept / mental model
Think of your Linux system as a busy apartment building. Each network service is a resident, and each open port is a door with a specific number (like 80 for HTTP or 5432 for PostgreSQL). A socket is the doorbell and mailbox combined—it's how traffic finds its way in and out.
When a service wants to be reachable, it creates a listening socket. This is like a doorman waiting at a designated entrance. The socket has three critical attributes:
- Protocol: TCP (reliable, connection-oriented) or UDP (fast, no guarantees)
- Address: The IP address and port number it's bound to (e.g.,
0.0.0.0:80means all interfaces, port 80) - State: For TCP, the state can be LISTEN, ESTABLISHED, TIME_WAIT, etc.
The kernel maintains a table of all sockets in real time. Both ss and netstat query this table, but ss is the modern, faster, and more detailed tool, while netstat is the older classic that's still ubiquitous.
Think of ss as the newer, more efficient doorman with a better walkie-talkie, and netstat as the veteran who knows every trick but takes a little longer to report.
How it works step by step
1. Start with listening sockets
The most common task is to see which services are listening for connections. This tells you what's ready to accept traffic.
For ss, the command is:
ss -tlnp
-t— show TCP sockets-l— show only listening sockets-n— show numeric addresses and ports (no DNS lookups)-p— show the process that owns the socket (requires root or matching user)
For netstat, the equivalent is:
netstat -tlnp
The -p flag will show the process name and PID. Here's example output (from ss):
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=812,fd=3))
LISTEN 0 128 127.0.0.1:5432 0.0.0.0:* users:((postgres,pid=1024,fd=7))
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:((nginx,pid=1109,fd=8))
Notice the Local Address column: 0.0.0.0:22 means SSH listens on all interfaces, while 127.0.0.1:5432 means PostgreSQL only accepts local connections. This distinction is crucial for security and troubleshooting.
2. Add UDP and other protocols
TCP and UDP are not the only protocols. UDP sockets don't have a listening state, but they still bind to ports. For both:
ss -ulnp # UDP listening sockets
ss -tlnp # TCP listening (as before)
ss -xln # Unix domain sockets (local IPC)
You can combine flags, e.g., ss -tulnp shows both TCP and UDP listening sockets.
3. Filter by port or process
Real systems have dozens of open ports. Filtering is essential.
To find which process owns a specific port (e.g., 8080):
ss -tlnp | grep :8080
# or
ss -tlnp sport = :8080
With netstat:
netstat -tlnp | grep :8080
To filter by process name (e.g., nginx):
ss -tlnp | grep nginx
Use head to limit output: ss -tlnp | head -20.
4. Check established connections and see all sockets
Listening sockets are only half the story. To monitor active connections (e.g., to ensure your service is being hit):
ss -tnp # all TCP sockets, including established
netstat -tnp
To see everything (all protocols, listening and connected):
ss -tanp
# or
netstat -tanp
The state column shows ESTABLISHED, TIME_WAIT, CLOSE_WAIT, etc., which are useful for diagnosing connection issues.
Hands-on walkthrough
Let's put it all together with a concrete exercise. We'll start a simple HTTP server, inspect its listening socket, then identify and debug a port conflict.
Step 1: Start a test server
Open a terminal and run a simple Python HTTP server on port 8000:
python3 -m http.server 8000
Keep it running in the background (or in a separate terminal).
Step 2: Verify the server is listening
In another terminal, run ss and grep for port 8000:
ss -tlnp | grep :8000
Expected output (your PID will differ):
LISTEN 0 128 0.0.0.0:8000 0.0.0.0:* users:((python,pid=1234,fd=5))
Now use netstat to confirm it matches:
netstat -tlnp | grep :8000
Step 3: Identify the owning process
Using the PID from the output, confirm the process name with ps:
ps -p 1234 -o pid,comm
Step 4: Simulate a port conflict
Try to start another server on the same port. Use a different document root:
python3 -m http.server 8000 --directory /tmp
You'll see an error like OSError: [Errno 98] Address already in use. This is the classic port already in use problem. To fix it:
- Identify the process using the port:
ss -tlnp | grep :8000 - Terminate it gracefully (or forcibly if needed):
bash kill 1234 # Wait a moment, then if it doesn't die: kill -9 1234 - Restart your service on the now-free port.
Step 5: Check established connections (optional)
If you open a browser to http://localhost:8000, then run:
ss -tnp | grep :8000
You'll see a ESTABLISHED row for the active connection.
Compare options / when to choose what
While both tools inspect ports, they have differences that matter in practice:
| Feature | ss |
netstat |
|---|---|---|
| Speed | Fast (reads /proc/net directly) |
Slower (parses more data) |
| Performance with many sockets | Excellent | Becomes sluggish with a few thousand connections |
| Output detail | More columns (e.g., send-q, recv-q) |
Fewer columns sometimes |
| Availability | Preinstalled on most modern distros | Deprecated on some (e.g., iproute2 is preferred) |
| Syntax | ss -tlnp sport = :8080 |
netstat -tlnp | grep :8080 |
| Filtering capabilities | sport, dport, address filters |
Only via grep/awk |
When to use ss: It's the default choice on modern Linux. It's faster, more scriptable, and shows more detailed socket info. For any serious troubleshooting, prefer ss.
When to use netstat: You'll need it on older systems or minimal containers where ss isn't installed. Also, some legacy scripts still use netstat; knowing its flags is essential for maintenance.
In short: use ss going forward, but keep netstat in your back pocket for compatibility.
Troubleshooting & edge cases
Permission denied showing process info
If you run ss -tlnp as a normal user, you may see users:( without the process name. The kernel restricts process info to the owner or root. Fix: prepend sudo, e.g., sudo ss -tlnp, or use ss -tln (which omits process info).
Performance hit with many connections
On a busy server with tens of thousands of sockets, netstat can take seconds, while ss is nearly instantaneous. If netstat feels slow, switch to ss. Also, always use -n (numeric) to skip DNS lookups—they add latency.
Port appears in TIME_WAIT but not LISTEN
After a service stops, you may see sockets in TIME_WAIT. That's normal—the kernel keeps them for a short time (usually 60 seconds) to handle late packets. Your new service may fail with Address already in use if it tries to bind the same port immediately. Wait a bit, or set SO_REUSEADDR in your app.
IPv6 vs IPv4
You might see [::]:80 in output for a socket that accepts both IPv4 and IPv6. If you need IPv4-specific info, look for 0.0.0.0:80. Services bound only to IPv6 won't accept IPv4 connections unless mapped.
netstat not found
In some minimal images (e.g., Alpine), netstat may not be installed. Install it with apk add net-tools or apt install net-tools on Debian/Ubuntu. Prefer ss to avoid dependency issues.
What you learned & what's next
You've mastered the core skill of inspecting ports on Linux. You can now:
- Explain why listening sockets matter and how the kernel tracks them
- Use
ss -tlnpto list all TCP listening sockets with process ownership - Use
netstat -tlnpas a compatible alternative - Filter by port or process to pinpoint conflicts
- Diagnose and resolve
Address already in useerrors
This skill is fundamental for understanding how traffic flows into your services—which ties directly into the next lesson in this track: health-checking HTTP endpoints. When you inspect ports, you're answering the question, "Is something listening?" The next lesson extends that to, "Is it actually responding correctly?"
By the end of this track, you'll be able to trace a request from DNS to a listening socket to a healthy HTTP response—the entire backbone of observability.
Practice recap
Start your Python HTTP server, then inspect its listening socket with ss -tlnp. Try to bind the same port with another service, observe the error, and resolve it by killing the first process. Then check TCP states after making a request with ss -tan | grep :8000.
Common mistakes
- Forgetting the
-pflag leads to missing process info; always include it (andsudoif needed) when you need to identify the owner. - Using
netstatwithout-ncauses slow DNS lookups and numeric ports hijacked by hostnames; almost never skip-n. - Relying on
grepto filter output can miss lines if your pattern is too broad; use exact ports like:8080or usesport = :8080filters withss.
Variations
- Use
lsof -i :8080as an alternative to identify which process owns a port, though it can be slower on active systems. - For deeper inspection of socket states, use
ss -tan state establishedto isolate active connections from the noise. - In containerized environments, run
ssfrom inside the container (using the host's/procis not reliable) or usensenterto inspect the host's networking namespace.
Real-world use cases
- Debugging a web server that fails to start because port 80 is already taken by an orphaned process.
- Auditing a hardened server to list all listening ports and confirm no unexpected services are exposed.
- Monitoring a microservices environment to verify each container's service is listening on the expected port after deployment.
Key takeaways
- Listening sockets are the entry points to your services; inspecting them is the first step in network troubleshooting.
ssis the modern, faster, and more scriptable tool;netstatis the legacy fallback for older systems.- Always use
-tlnpto see TCP listening sockets with numeric addresses, ports, and process ownership. - Filter by exact port or process with
ss -tlnp | grep :8080to quickly isolate conflicts. - Permission issues are common; run
sudo ss -tlnpto see process names for all sockets.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.