The netstat and ss commands show which ports your Linux server is listening on and which connections are currently open. Run ss -tulpn first: it lists every TCP and UDP socket, the port it uses and the process that owns it, which answers most "what is on port 8080?" questions in one line.
ss is the modern tool and ships with every current distribution. netstat comes from the older net-tools package, is often missing on fresh installs, and prints similar output with different flags. You will use both here, because plenty of tutorials and runbooks still quote netstat commands.
Before you start
You need:
- A Linux VPS running Ubuntu 22.04/24.04, Debian 12, or AlmaLinux/Rocky 8–9.
- SSH access with a sudo-capable user. If you are not connected yet, see connect to your Linux VPS with SSH.
- Root or
sudorights, because seeing the process name behind a socket owned by another user requires elevated privileges.
Check whether netstat exists:
command -v netstat || echo "netstat not installed"If it is missing, install the package:
# Ubuntu / Debian
sudo apt update && sudo apt install -y net-tools
# AlmaLinux / Rocky
sudo dnf install -y net-toolsTip: ss is part of iproute2, which is installed by default. On a minimal image, add it with sudo apt install -y iproute2 or sudo dnf install -y iproute.
List all listening ports with ss and netstat
Start with the listening sockets. These are the entry points into your server: SSH, a web server, a database, or something you did not expect.
sudo ss -tulpnFlag by flag: -t TCP, -u UDP, -l listening only, -p show the process, -n numeric ports and addresses instead of names. A typical line looks like this:
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=741,fd=3))
LISTEN 0 511 *:80 *:* users:(("nginx",pid=1204,fd=6))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=741,fd=4))Read it as: protocol, receive queue, send queue, local address and port, peer address, then the process. 0.0.0.0:22 means listening on every IPv4 interface; 127.0.0.1:5432 means only reachable from the machine itself, which is what you want for a database.
The netstat equivalent:
sudo netstat -tulpnSame columns, different order — local address comes before the peer. If you only need TCP, drop -u. If you want the interface names too, add -i to netstat or use ss -tulpn -o for timer state.
To see just one port:
sudo ss -tlnp 'sport = :22'
sudo netstat -tlnp | grep ':22'Warning: A service listening on 0.0.0.0 is reachable from the internet unless a firewall blocks it. Confirm what is exposed before assuming the firewall covers it, and compare with your rules in UFW vs firewalld vs nftables vs iptables.
Inspect active and established connections
Listening ports only show the doors. Established connections show who is currently inside.
sudo ss -tanThat prints every TCP socket including ESTABLISHED, TIME-WAIT and CLOSE-WAIT. To narrow to live sessions:
sudo ss -tnp state established
sudo netstat -tnp | grep ESTABLISHEDYou can filter by remote host, which is useful when one address is hammering your service:
sudo ss -tn state established '( dport = :80 or sport = :80 )'
sudo ss -tn dst 203.0.113.10Count connections per remote IP to spot a flood:
sudo ss -tn state established | awk 'NR>1 {print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | headFor a one-screen summary of totals by protocol, use ss -s (netstat users can run netstat -s for the kernel counters, which is much longer output):
ss -sFind which process owns a port
When a port is busy and a service refuses to start, you need the PID and the binary behind it.
sudo ss -tulpn | grep ':8080'
sudo lsof -i :8080ss gives you users:(("java",pid=9821,fd=88)). Check what that process actually is before you touch it:
ps -fp 9821
sudo ls -l /proc/9821/exeFor a listening socket that belongs to a container, the process may appear as docker-proxy or live in a different network namespace. In that case inspect the container directly with docker ps and docker port <container>; see Docker persistent storage for non-root users if you are sharing volumes between containers and the host.
If the port is genuinely held by a stale process you own, stop it through its service manager first:
sudo systemctl stop myapp
sudo systemctl status myappOnly kill by PID when the process has no unit:
sudo kill 9821
sudo kill -9 9821Warning: kill -9 skips cleanup. For databases and mail servers it can leave files in an inconsistent state. Try systemctl stop, then plain kill, before -9.
Watch connections live
For intermittent problems — a health check that fails every few minutes, a client that reconnects in a loop — sampling is better than a one-off snapshot.
watch -n 2 'ss -tn state established | tail -n +2 | wc -l'For a scrolling stream of new sockets as they appear:
sudo ss -tnpRerunning the command in a loop with watch is the safest option on a production server. You can also confirm a specific port answers from outside using plain bash, without installing anything:
timeout 5 bash -c '</dev/tcp/203.0.113.10/80' && echo "port open" || echo "port closed"Check it works
Work through these in order and you will have a full picture of the server's network surface:
sudo ss -tulpnlists every listening socket with a process name. No?in the process column means you had enough privileges.sudo ss -tnp state establishedshows current sessions with both endpoints and the owning process.ss -sreports totals that roughly match what you saw in the detailed listings.- Each port you expect (SSH, HTTP/HTTPS, your application) appears with the correct bind address, and nothing you cannot identify is listening on
0.0.0.0. - A connection test from your workstation (
nc -zv 203.0.113.10 22or the bash/dev/tcpcheck above) succeeds for ports you intend to expose and fails for the rest.
If a service is bound to 127.0.0.1 but you expected it to be public, that is a service configuration issue, not a firewall one. Change the listen directive in the service config, restart it, and recheck with ss.
Troubleshooting
ss: command not foundornetstat: command not found→ the package is missing on a minimal image. Installiproute2forssornet-toolsfornetstatwith your distribution's package manager, then rerun the command.- Process column shows a dash or
?→ you ran the command withoutsudo. Ordinary users only see their own sockets, so always usesudo ss -tulpn. Address already in usewhen starting a service → another process holds the port. Find it withsudo ss -tlnp 'sport =:8080', stop the owning unit, then start yours.- Port is listening locally but unreachable from outside → either the service binds only to
127.0.0.1, or the firewall drops the traffic. Check the bind address inssoutput first, then review your firewall rules. ssoutput is very long or hard to read → filter by state or port instead of scrolling:sudo ss -tn state established,sudo ss -tlnp 'sport =:443', or pipe throughgrep. Use-nso ports stay numeric.- A connection sits in
CLOSE-WAITfor a long time → the remote end closed and your application has not released the socket. That is an application bug, not a kernel setting; restart the service and check its logs.
Tip: Do not reach for ss -K (killing sockets) as a first response. It drops connections without telling the application, which often makes the problem harder to diagnose. Stop or restart the service instead.
Next steps
- Get a baseline of CPU, memory and logs alongside your connection checks with monitor CPU, memory, disk and logs on a Linux server.
- Block an abusive address at scale after you have identified it in
ssoutput using ipset for high-performance IP blacklisting. - Lock down the ports you found exposed by following configure firewalld on AlmaLinux and Rocky Linux or the UFW steps for Debian and Ubuntu.
- Still setting the server up? Start from first steps after your Linux VPS is ready.
