8080
The port a dev server, a Tomcat instance or a proxy grabs when 80 is taken — and the one that is already taken when you need it.
Run the line for your platform. Each one names the process holding the port and the address it bound to, which is usually the whole answer.
lsof -nP -iTCP:8080 -sTCP:LISTENPrints the command name, PID and user of whatever holds the port, plus the address it bound. `127.0.0.1:8080` means the server is loopback-only, which is exactly why your phone on the same Wi-Fi cannot reach it; `*:8080` means every interface. No output means the port is genuinely free. Add `sudo` when the listener belongs to another user, and note that `Docker` or `com.docker.backend` in the command column means a published container port, so stop the container rather than killing that process.
sudo ss -tlnp 'sport = :8080'`-t` selects TCP, `-l` listening sockets only, `-n` numeric ports, `-p` the owning process as `users:(("java",pid=8123,fd=50))`. Read the local address column carefully: `[::]:8080` is the IPv6 wildcard and usually accepts IPv4 too, while a separate `0.0.0.0:8080` row means a second process took the IPv4 side. Without `sudo` the process column is blank for anything you do not own.
netstat -ano | findstr :8080The last column is the PID that owns the socket; resolve it with `tasklist /FI "PID eq 8123"`. `findstr` matches substrings, so `:8080` also matches `:8080` inside a remote address and `:80800` were it possible — confirm the local address column before acting. An empty result while a bind still fails points at the reserved-range case below.
netsh interface ipv4 show excludedportrange protocol=tcpRun this when nothing is listening on 8080 and the bind still fails. Hyper-V, WSL2, Docker Desktop and the Windows NAT service reserve blocks of dynamic ports at boot, and a reservation that happens to cover 8080 makes every bind fail with no process to blame. If 8080 falls inside a listed range, either restart the host service that claimed it or move your server to a port outside every range.
`nmap -Pn -p 8080 example.com` answers whether anything is reachable there from outside the machine. `open` means the TCP handshake completed, `closed` means the host sent a reset so it is up with nothing listening, and `filtered` means the probe vanished — a firewall dropped it. Add `--reason` to see which of those actually happened, and `-sV` to have nmap fetch a response and report the server banner, which is how you find out that the thing on 8080 is a Tomcat you forgot about. Only scan hosts you are responsible for.
| What you see | What it means |
|---|---|
Error: listen EADDRINUSE: address already in use :::8080 | Another process already holds the port. The `:::8080` form is the IPv6 wildcard, which on most systems also covers IPv4, so a server bound there blocks both. Find the owner with the probe for your platform; nine times out of ten it is an earlier run of the same server that was detached from the terminal rather than stopped. |
Only one usage of each socket address (protocol/network address/port) is normally permitted | The Windows wording of the same thing, error 10048. If `netstat` shows no listener, the port is reserved rather than occupied: check the excluded ranges with the `netsh` command above before hunting for a process that does not exist. |
The server says it started, but another device on the same network gets connection refused | It bound to `127.0.0.1` instead of `0.0.0.0`. Many frameworks changed to a loopback default deliberately, so this is a configuration flag rather than a bug — `--host 0.0.0.0`, `HOST=0.0.0.0`, or the equivalent. Confirm with the probes: the address column tells you which one it chose. |
The port frees itself about a minute after the server exits | Sockets that were closed while a connection was still open sit in `TIME_WAIT` for up to two minutes, and a new bind is refused until then unless the program sets `SO_REUSEADDR`. Waiting works; so does starting the new server on a different port. Nothing needs killing. |
8080 answers, but with something you did not start | Usually a container that is still publishing the port — `docker ps` will show `0.0.0.0:8080->8080/tcp` on the offender. It can also be an installed service such as Tomcat or a printer admin interface that starts at boot. `nmap -sV` against your own machine reads the banner and names it. |
IANA registers 8080 as `http-alt`, "HTTP Alternate", for both TCP and UDP, so the number is assigned even though what actually runs there is convention rather than a protocol. The reason it became the default for so much software is mundane: on Unix systems ports below 1024 are privileged, so binding port 80 needs root or a granted capability, while 8080 needs neither. That makes it the natural home for anything a developer starts by hand — Tomcat's default connector, Jenkins, Spring Boot, a Vite or webpack proxy, Docker's published port for a web container, and the origin behind a reverse proxy that terminates TLS on 443. The number implies plain HTTP and nothing else: it is not encrypted, it carries no special meaning to a browser, and `http://host:8080/` is simply an ordinary HTTP request to an unusual port. Because it is shared by so much software, an occupied 8080 is far more often another one of your own processes than anything mysterious — the probes below name it in one line.
Moving a service from 80 to 8080 buys no security whatsoever. Internet-wide scanners sweep 8080 as routinely as 80, and a service there is discovered in the same hours. What makes 8080 riskier in practice is what tends to live on it: admin consoles and dev servers that were never meant to face outward — Tomcat's manager application, a Jenkins instance with a fresh setup wizard, a Spring Boot actuator endpoint, a container's debug UI. Bind development servers to `127.0.0.1` so they cannot leave the machine, and expose anything real through a reverse proxy on 443 that terminates TLS, rather than opening 8080 at the firewall. If it must be reachable, restrict it by source address and put authentication in front of it; treat it as plaintext, because it is.
All of these run in your browser — nothing is uploaded.
Ports people usually end up checking in the same session as 8080.
Almost always permissions. lsof and ss list the socket but hide the owning process when it belongs to another user, so a server started by a system account, by a container runtime or by launchd shows up as an anonymous listener until you re-run the command with sudo. On Windows the opposite happens: netstat shows nothing at all, yet a bind still fails, which means the port sits inside a range that Hyper-V, WSL2 or Docker Desktop reserved at boot.
Look at what it is first. An orphaned dev server is safe to kill; a database with a write in flight is not, and neither is a system service that something else depends on. If the owner is a container, stop the container instead — killing the process the host sees only makes the runtime restart it. When the process is something you need, moving your own service to another port is the faster fix.
The listener bound to 127.0.0.1 rather than 0.0.0.0. The probes on every page print the bound address next to the process for exactly this reason: a loopback bind is reachable from the machine itself and from nowhere else, no matter how the firewall is configured. Many frameworks made that the default deliberately, so it is usually a flag rather than a bug. If the address is already 0.0.0.0 and it still fails, the next suspect is a host firewall or a cloud security group.
Barely. Internet-wide scanners sweep the whole port range continuously, so an unusual number buys hours, not safety, and it costs you every colleague who now has to remember it. It does cut the noise in your logs from opportunistic scans of the default port, which is a real if modest benefit. Authentication, a firewall that restricts source addresses, and not exposing the service at all are the measures that actually change the outcome.
Still stuck on port 8080? browse every port in the reference — or go back to the failure message you actually got, above.