Knowing which ports are open — and which program owns each one — is basic server hygiene. It answers “why can’t I start Nginx?”, “is MySQL exposed to the internet?” and “what is this unknown process listening on 4444?”. Here are the tools, from the one you’ll use daily to the outside-in view.

ss — the modern default

sudo ss -tulpn
  • -t TCP, -u UDP
  • -l listening sockets only
  • -p show the process (needs sudo for other users’ processes)
  • -n numeric ports and addresses (don’t resolve names — faster and clearer)

Example 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=512,fd=13))
tcp   LISTEN 0      511          0.0.0.0:80         0.0.0.0:*     users:(("nginx",pid=812,fd=6))
tcp   LISTEN 0      128          0.0.0.0:22         0.0.0.0:*     users:(("sshd",pid=701,fd=3))
tcp   LISTEN 0      151        127.0.0.1:3306       0.0.0.0:*     users:(("mysqld",pid=901,fd=21))
tcp   LISTEN 0      4096               *:9100             *:*     users:(("node_exporter",pid=1203,fd=3))

How to read Local Address:

Address Reachable from
0.0.0.0:PORT All IPv4 interfaces — the internet, unless firewalled
[::]:PORT or *:PORT All interfaces, IPv6 (and often IPv4 too)
127.0.0.1:PORT, [::1]:PORT This machine only
10.x.x.x:PORT That specific (private) interface

In the example, MySQL is safely on localhost, but node_exporter on *:9100 is exposed to everyone unless the firewall blocks it.

Handy ss filters

sudo ss -tlnp 'sport = :443'            # who listens on 443
sudo ss -tnp state established          # current connections
sudo ss -tn state established '( dport = :443 or sport = :443 )' | wc -l   # count HTTPS connections
ss -s                                   # summary statistics

Which process uses a port?

sudo ss -tlnp | grep ':8080'
sudo lsof -i :8080
sudo fuser -v 8080/tcp

lsof -i :PORT also shows established connections, not just the listener. Once you have the PID:

ps -fp 1234
systemctl status 1234          # shows which systemd unit the PID belongs to

This is how you solve Address already in use errors: find the process holding the port, then stop or reconfigure it.

netstat — the classic

netstat comes from the older net-tools package, not installed by default on modern distributions:

sudo apt install net-tools
sudo netstat -tulpn

Same flags, similar output. Everything it does, ss does faster.

lsof — open files, including sockets

sudo lsof -i -P -n | grep LISTEN
sudo lsof -i TCP:22
sudo lsof -i @203.0.113.5            # connections to/from an IP
sudo lsof -p 1234 -i                 # network files of one process

The firewall view

A listening port isn’t necessarily reachable — the firewall decides:

sudo ufw status verbose               # Ubuntu/Debian with UFW
sudo firewall-cmd --list-all          # firewalld
sudo nft list ruleset                 # nftables
sudo iptables -L -n -v                # iptables

Check from outside with nmap

The only true test is from another machine:

sudo apt install nmap
nmap 203.0.113.10                     # top 1000 TCP ports
nmap -p- 203.0.113.10                 # all 65535 TCP ports (slower)
nmap -sU -p 53,123,51820 203.0.113.10 # specific UDP ports (needs sudo)
nmap -sV -p 22,80,443 203.0.113.10    # identify service versions

States:

  • open — a service answered.
  • closed — the host answered, but nothing listens there.
  • filtered — no answer; a firewall dropped the probe.

Only scan systems you own or are authorised to test. Your provider may treat outbound scanning of other networks as abuse.

Quick single-port tests without nmap:

nc -zv 203.0.113.10 443
timeout 3 bash -c '</dev/tcp/203.0.113.10/443' && echo open

A quick security audit

  1. sudo ss -tulpn — list everything listening.
  2. For each line on 0.0.0.0/[::]/*: should this be public?
  3. Databases, caches (Redis, Memcached), admin panels and monitoring exporters should bind to 127.0.0.1 or a private network.
  4. Confirm with an external nmap that only intended ports are open.
  5. Repeat after installing new software — packages sometimes start services listening publicly by default.

Exposed Redis, MongoDB and Elasticsearch instances are among the most common causes of server compromise. If you find one, fix the bind address and add a firewall rule today — see opening and closing ports and the UFW guide.

Also useful: port scanning defenses and the lsof command.

  • Linux
  • Networking
  • Security