How to check open ports on Linux with ss and nmap
List the ports your server listens on with ss, find which program owns each one, then scan the server from outside with nmap to see what the internet can reach.
Tested on Ubuntu 24.04 LTS
Before you start
- A Linux server
- Root or a user with sudo
- A second machine (your computer) for the outside scan
To check which ports are open on a Linux server, run sudo ss -tulpn: it lists every TCP and UDP port the server listens on, the address it's bound to, and the program behind it. To see which of those the internet can actually reach through your firewalls, scan the server from another machine with nmap 203.0.113.10.
You need both views. ss shows what's listening; nmap from outside shows what gets through. A database listening on 0.0.0.0:5432 with the firewall blocking it is fine; the same database reachable from the internet isn't. Only scan servers you own or have permission to scan: scanning other people's machines can break your provider's terms and, in some places, the law.
We ran every command here on Ubuntu 24.04 with nginx and OpenSSH installed. ss is part of iproute2 and preinstalled on every current distribution.
List listening ports with ss
sudo ss -tulpnThe letters: t TCP, u UDP, l listening only, p show the process (needs sudo), n numeric ports instead of names. Trimmed output:
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=68,fd=14))
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1456,fd=5))
tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1108,fd=3))
tcp LISTEN 0 5 127.0.0.1:8000 0.0.0.0:* users:(("python3",pid=3187,fd=3))
tcp LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=1456,fd=6))The column that matters is Local Address:
| Bound to | Who can connect |
|---|---|
0.0.0.0:80 or [::]:80 (or *:80) |
Any address, IPv4 or IPv6: the internet, unless a firewall stops it |
203.0.113.10:80 |
Only via that one address |
127.0.0.1:8000 or [::1]:8000 |
Only programs on the server itself |
127.0.0.53%lo:53 |
The local DNS resolver; normal on Ubuntu |
On a fresh server you'll typically see SSH on 22 and the local resolver. Everything else should be something you installed on purpose. Anything that doesn't need outside connections, such as a database used only by an app on the same server, an admin dashboard or a metrics endpoint, is safer bound to 127.0.0.1.
Only TCP ports:
sudo ss -tlnpFind out what owns a port
Filter ss on one port:
sudo ss -tlnp 'sport = :80'Or ask lsof:
sudo lsof -i :80 -sTCP:LISTENWith the PID, find the program and the systemd service it belongs to:
ps -o pid,user,cmd -p 1108
systemctl status 1108systemctl status accepts a PID and shows the unit that owns it, which is the one to stop, disable or reconfigure.
Check from outside with nmap
Run this from your own computer, not from the server: a scan from the server itself goes around the firewall and tells you nothing about what the internet sees. Install nmap (sudo apt install nmap, brew install nmap, or the Windows installer from nmap.org), then:
nmap -Pn 203.0.113.10-Pn skips the "is the host up?" ping, which a firewall may block. Without a port list, nmap checks the 1,000 most common TCP ports:
PORT STATE SERVICE
22/tcp open ssh
80/tcp open httpWhat the states mean:
| State | Meaning |
|---|---|
open |
Something listens and the firewall lets you in |
closed |
Nothing listens there, but the packet reached the server |
filtered |
No answer at all: a firewall dropped the packet |
Check specific ports, including ones outside the top thousand:
nmap -Pn -p 22,80,443,5432,8080 203.0.113.10UDP ports (WireGuard, DNS) need a UDP scan, which needs root on your computer and is slower:
sudo nmap -Pn -sU -p 51820,53 203.0.113.10UDP results are less clear-cut: open|filtered is common, because a service like WireGuard doesn't answer packets it doesn't recognise. ss -ulpn on the server is the better check for UDP.
Scan IPv6 too, if the server has an address; the firewall rules are separate:
nmap -6 -Pn 2001:db8::10A quick single-port test
To check one TCP port without nmap, nc (netcat) is enough:
nc -zv 203.0.113.10 443succeeded means open; Connection refused means closed; no answer until the timeout means filtered.
Close what shouldn't be open
For each port that's open but shouldn't be, pick one:
- Stop the service if you don't need it:
sudo systemctl disable --now SERVICE. - Bind it to localhost in its configuration (
bind-address = 127.0.0.1for MySQL and MariaDB,listen_addresses = 'localhost'for PostgreSQL), and restart it. - Block it in the firewall: ufw on Ubuntu and Debian, firewalld on AlmaLinux and Rocky Linux.
Then scan again from outside to confirm.
Troubleshooting
ss shows no process names. You ran it without sudo; other users' processes are hidden.
A port is open in nmap but ss doesn't show it. You might be scanning the wrong address, or Docker published the port: Docker's forwarding happens in the firewall, so docker-proxy or nothing at all appears in ss. docker ps shows the published ports.
Everything shows filtered. A firewall in front of the server drops all traffic from your address: the panel firewall, a cloud security group, or your own network blocking outgoing scans. Check from a second connection, such as a phone hotspot.
SSH shows as systemd in ss on Ubuntu 24.04. SSH is socket-activated there: systemd holds port 22 and hands connections to sshd. It's normal.
Next steps
- Lock down what's left with ufw or firewalld.
- Measure the connection itself: test your server's network.
Doing it on VPSNine
On VPSNine, the panel firewall filters traffic before it reaches the server. Ports it denies don't show as open in nmap, even when ss shows something listening on them, which is what you want for anything private. Outbound port 25 is blocked on new servers; that doesn't affect inbound scans (details). Works on Ubuntu 24.04 and every other image, on all plans.