Updated: 17 October 2026 · Applies to: Ubuntu 26.04 (tested: iproute2 6.19, nginx 1.28.3, Apache 2.4.66, Python 3.14, Node.js 22) and AlmaLinux 10 (iproute 6.17); the same ss commands work on Ubuntu 24.04, Debian 13 and Rocky Linux
To check if a port is in use on Linux, run ss -ltn 'sport = :8080' (replace 8080 with your port, and use -lun for a UDP port). If only the header line comes back, the port is free. A line with the state LISTEN (TCP) or UNCONN (UDP) means a program already holds it, and a second service on that port fails with "Address already in use". Run it as sudo ss -ltnp 'sport = :8080' to see which program it is.
1. Check a port before you start a service
- Check the TCP port. Lab example from our Ubuntu 26.04 test container, first with nothing on port 8080, then with a test web server on it:
$ ss -ltn 'sport = :8080' State Recv-Q Send-Q Local Address:Port Peer Address:Port $ sudo ss -ltnp 'sport = :8080' State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess LISTEN 0 5 127.0.0.1:8080 0.0.0.0:* users:(("python3",pid=1334,fd=3))The first result (header only) means free. The second shows the port in use bypython3with process ID 1334. - For a UDP port, such as a game server or DNS, use
-lun. UDP has no LISTEN state; a bound UDP port showsUNCONN:$ sudo ss -lunp 'sport = :5353' State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess UNCONN 0 0 127.0.0.1:5353 0.0.0.0:* users:(("python3",pid=1419,fd=3)) - In a script, add
-H(no header), so any output at all means "in use":if ss -Hltn 'sport = :8080' | grep -q .; then echo "8080 in use"; else echo "8080 free"; fi
The letters mean: l listening sockets, t TCP, u UDP, n port numbers instead of names, p show the program. Without sudo, ss still shows that the port is taken, but the Process column stays empty for programs owned by other users. On old systems, sudo netstat -tlnp | grep ':8080 ' gives the same answer if net-tools is installed (see netstat: command not found).
2. Read the port status
To see every TCP connection on a port, not only the listener, drop the -l:
$ ss -tan 'sport = :8080 or dport = :8080' State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 5 127.0.0.1:8080 0.0.0.0:* TIME-WAIT 0 0 127.0.0.1:38180 127.0.0.1:8080 ESTAB 0 0 127.0.0.1:36336 127.0.0.1:8080 ESTAB 0 0 127.0.0.1:8080 127.0.0.1:36336
- LISTEN: a program is waiting for connections on this port. This is what blocks a second service.
- ESTAB (established): an open connection between a client and the service.
- TIME-WAIT: a connection that has just closed. Linux keeps it for about a minute (just over 60 seconds in our test).
- UNCONN: a UDP port that is bound. UDP has no connections, so this is the only state you see.
3. Quick test without ss: bash or nc
You can also try to connect. Bash can open a TCP connection by itself through /dev/tcp:
timeout 3 bash -c '</dev/tcp/127.0.0.1/8080' && echo "in use" || echo "free"
On a free port, bash prints bash: connect: Connection refused (plus a second line naming /dev/tcp/127.0.0.1/8080) and then free. With netcat:
$ nc -zv -w 2 127.0.0.1 8080 Connection to 127.0.0.1 8080 port [tcp/http-alt] succeeded! $ nc -zv -w 2 127.0.0.1 8080 nc: connect to 127.0.0.1 port 8080 (tcp) failed: Connection refused
A connection test only reaches the address you give. A program bound to the server's public IP alone does not answer on 127.0.0.1, yet it still holds the port, so ss is the more reliable check.
4. Why a service says "Address already in use"
Error number 98 (EADDRINUSE) means another socket already holds that address and port. This is how it looked in our test when port 8080 was already taken:
nginx: [emerg] bind() to 127.0.0.1:8080 failed (98: Address already in use) nginx: [emerg] still could not bind() (98)Address already in use: AH00072: make_sock: could not bind to address 127.0.0.1:8080 no listening sockets available, shutting down OSError: [Errno 98] Address already in use Error: listen EADDRINUSE: address already in use 127.0.0.1:8080
Those are nginx, Apache, Python and Node.js. Note that nginx -t still said "syntax is ok": a configuration test does not check whether the port is free. To fix it:
- Find what holds the port:
sudo ss -ltnp 'sport = :8080'. - If it is an old copy of the same service, or a service you do not need, stop it properly with systemctl. Step-by-step: find the process using a port in Linux and stop it.
- If both programs are needed, give one of them another port in its configuration.
- If
ss -ltnshows no listener but the error stays, checkss -tan 'sport = :8080'for TIME-WAIT. A program that restarts without theSO_REUSEADDRsocket option cannot bind while old connections are in TIME-WAIT. In our test, a small Python server without it got Errno 98 straight after a restart and started normally once the TIME-WAIT entries were gone, about a minute later. nginx and Apache set it: in our test both restarted at once with TIME-WAIT entries on their port, so this mostly affects your own scripts and applications.
A different error, PermissionError: [Errno 13] Permission denied or Permission denied from bind, means a normal user tried to use a port below 1024. The limit is in net.ipv4.ip_unprivileged_port_start (1024 in our test). Run the service as root and let it drop privileges, use a port above 1023, or put a web server in front.
5. Choose a free port for a new service
- Avoid the range Linux uses for outgoing connections:
cat /proc/sys/net/ipv4/ip_local_port_rangeprinted32768 60999in our test. A service on one of those ports can clash with a random outgoing connection. - Find the first free port in a range:
for p in $(seq 8080 8090); do ss -Htln "sport = :$p" | grep -q . || { echo "first free port: $p"; break; }; done getent services 8080shows the name the port is registered under (http-alt 8080/tcp webcache). That is only a naming list: it does not tell you whether anything uses the port on your server.
6. In use on the server, but closed from outside?
A port can be in use on the server and still be unreachable from the internet: the program may listen on 127.0.0.1 only, or a firewall may block it. Test from another computer with nc -zv SERVER_IP PORT. For the full list of listening ports and an outside check, see how to check open ports in Linux and how to test port connectivity with nc. For every option of ss, lsof and fuser, see find which process is using a port (ss, lsof, fuser).
Official documentation: ss manual page.